chore: optimise first evaluation by adding worker scope cache (CE) (#38068)
## Description - Add evalContextCache to reduce the number of times the evalContext is created from the contextTree - remove resetWorkerGlobalScope execution before every evalSync - Instantiate the evalWorker in src/index.tsx Fixes #`Issue Number` _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.All" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/12295048843> > Commit: 04b1e859b02282ba9efa96aea25acc9f20098061 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12295048843&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.All` > Spec: > <hr>Thu, 12 Dec 2024 12:21:04 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new property `actionNames` to enhance action tracking within JavaScript action entities. - Added a new feature flag `release_evaluation_scope_cache` for improved feature management. - Implemented a new function `isPropertyAnEntityAction` to identify action properties in JavaScript entities. - Enhanced the `loadAppEntities` method to improve JavaScript library loading processes. - Updated the evaluation context initialization process to utilize `getDataTreeContext`. - Expanded the `WIDGET_CONFIG_MAP` to include detailed configurations for various widget types. - **Bug Fixes** - Enhanced error handling for unsafe function calls in evaluation logic. - Improved error handling and logging for library installation and uninstallation processes. - **Tests** - Expanded test coverage for action bindings and dependencies in the `DataTreeEvaluator`. - Updated tests to validate the new `actionNames` property and its integration. - Modified tests to ensure correct functionality of the `evaluateSync` function. - Added new test cases to assess the behavior of the evaluator with widget updates. - **Chores** - Streamlined imports and initialization of worker instances across various files. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
4ea5021e88
commit
abb0878388
|
|
@ -191,6 +191,7 @@ describe("generateDataTreeJSAction", () => {
|
||||||
myVar1: "SMART_SUBSTITUTE",
|
myVar1: "SMART_SUBSTITUTE",
|
||||||
myVar2: "SMART_SUBSTITUTE",
|
myVar2: "SMART_SUBSTITUTE",
|
||||||
},
|
},
|
||||||
|
actionNames: new Set(["myFun2", "myFun1"]),
|
||||||
};
|
};
|
||||||
const resultData = generateDataTreeJSAction(jsCollection);
|
const resultData = generateDataTreeJSAction(jsCollection);
|
||||||
|
|
||||||
|
|
@ -389,6 +390,7 @@ describe("generateDataTreeJSAction", () => {
|
||||||
myVar1: "SMART_SUBSTITUTE",
|
myVar1: "SMART_SUBSTITUTE",
|
||||||
myVar2: "SMART_SUBSTITUTE",
|
myVar2: "SMART_SUBSTITUTE",
|
||||||
},
|
},
|
||||||
|
actionNames: new Set(["myFun2", "myFun1"]),
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = generateDataTreeJSAction(jsCollection);
|
const result = generateDataTreeJSAction(jsCollection);
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ export const generateDataTreeJSAction = (
|
||||||
const dependencyMap: DependencyMap = {};
|
const dependencyMap: DependencyMap = {};
|
||||||
|
|
||||||
dependencyMap["body"] = [];
|
dependencyMap["body"] = [];
|
||||||
const actions = js.config.actions;
|
const actions = js.config.actions || [];
|
||||||
// TODO: Fix this the next time the file is edited
|
// TODO: Fix this the next time the file is edited
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const actionsData: Record<string, any> = {};
|
const actionsData: Record<string, any> = {};
|
||||||
|
|
@ -89,6 +89,7 @@ export const generateDataTreeJSAction = (
|
||||||
dynamicBindingPathList: dynamicBindingPathList,
|
dynamicBindingPathList: dynamicBindingPathList,
|
||||||
variables: listVariables,
|
variables: listVariables,
|
||||||
dependencyMap: dependencyMap,
|
dependencyMap: dependencyMap,
|
||||||
|
actionNames: new Set(actions.map((action) => action.name)),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ export interface JSActionEntityConfig extends EntityConfig {
|
||||||
moduleId?: string;
|
moduleId?: string;
|
||||||
moduleInstanceId?: string;
|
moduleInstanceId?: string;
|
||||||
isPublic?: boolean;
|
isPublic?: boolean;
|
||||||
|
actionNames: Set<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JSActionEntity {
|
export interface JSActionEntity {
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ export const FEATURE_FLAG = {
|
||||||
"release_table_custom_loading_state_enabled",
|
"release_table_custom_loading_state_enabled",
|
||||||
release_custom_widget_ai_builder: "release_custom_widget_ai_builder",
|
release_custom_widget_ai_builder: "release_custom_widget_ai_builder",
|
||||||
ab_request_new_integration_enabled: "ab_request_new_integration_enabled",
|
ab_request_new_integration_enabled: "ab_request_new_integration_enabled",
|
||||||
|
release_evaluation_scope_cache: "release_evaluation_scope_cache",
|
||||||
release_table_html_column_type_enabled:
|
release_table_html_column_type_enabled:
|
||||||
"release_table_html_column_type_enabled",
|
"release_table_html_column_type_enabled",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
@ -83,6 +84,7 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = {
|
||||||
release_table_custom_loading_state_enabled: false,
|
release_table_custom_loading_state_enabled: false,
|
||||||
release_custom_widget_ai_builder: false,
|
release_custom_widget_ai_builder: false,
|
||||||
ab_request_new_integration_enabled: false,
|
ab_request_new_integration_enabled: false,
|
||||||
|
release_evaluation_scope_cache: false,
|
||||||
release_table_html_column_type_enabled: false,
|
release_table_html_column_type_enabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,7 @@ export enum ExecutionType {
|
||||||
/**
|
/**
|
||||||
* This method returns new dataTree with entity function and platform function
|
* This method returns new dataTree with entity function and platform function
|
||||||
*/
|
*/
|
||||||
export const addDataTreeToContext = (args: {
|
export const getDataTreeContext = (args: {
|
||||||
EVAL_CONTEXT: EvalContext;
|
|
||||||
dataTree: Readonly<DataTree>;
|
dataTree: Readonly<DataTree>;
|
||||||
removeEntityFunctions?: boolean;
|
removeEntityFunctions?: boolean;
|
||||||
isTriggerBased: boolean;
|
isTriggerBased: boolean;
|
||||||
|
|
@ -50,10 +49,11 @@ export const addDataTreeToContext = (args: {
|
||||||
const {
|
const {
|
||||||
configTree,
|
configTree,
|
||||||
dataTree,
|
dataTree,
|
||||||
EVAL_CONTEXT,
|
|
||||||
isTriggerBased,
|
isTriggerBased,
|
||||||
removeEntityFunctions = false,
|
removeEntityFunctions = false,
|
||||||
} = args;
|
} = args;
|
||||||
|
const EVAL_CONTEXT: EvalContext = {};
|
||||||
|
|
||||||
const dataTreeEntries = Object.entries(dataTree);
|
const dataTreeEntries = Object.entries(dataTree);
|
||||||
const entityFunctionCollection: Record<string, Record<string, Function>> = {};
|
const entityFunctionCollection: Record<string, Record<string, Function>> = {};
|
||||||
|
|
||||||
|
|
@ -95,16 +95,23 @@ export const addDataTreeToContext = (args: {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeEntityFunctions)
|
if (removeEntityFunctions) {
|
||||||
return removeEntityFunctionsFromEvalContext(
|
removeEntityFunctionsFromEvalContext(
|
||||||
entityFunctionCollection,
|
entityFunctionCollection,
|
||||||
EVAL_CONTEXT,
|
EVAL_CONTEXT,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isTriggerBased) return;
|
return EVAL_CONTEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTriggerBased) {
|
||||||
|
return EVAL_CONTEXT;
|
||||||
|
}
|
||||||
|
|
||||||
// if eval is not trigger based i.e., sync eval then we skip adding entity function to evalContext
|
// if eval is not trigger based i.e., sync eval then we skip adding entity function to evalContext
|
||||||
addEntityFunctionsToEvalContext(EVAL_CONTEXT, entityFunctionCollection);
|
addEntityFunctionsToEvalContext(EVAL_CONTEXT, entityFunctionCollection);
|
||||||
|
|
||||||
|
return EVAL_CONTEXT;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addEntityFunctionsToEvalContext = (
|
export const addEntityFunctionsToEvalContext = (
|
||||||
|
|
|
||||||
|
|
@ -1104,6 +1104,19 @@ export const isNotEntity = (entity: DataTreeEntity) => {
|
||||||
export const isEntityAction = (entity: DataTreeEntity) => {
|
export const isEntityAction = (entity: DataTreeEntity) => {
|
||||||
return isAction(entity);
|
return isAction(entity);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isPropertyAnEntityAction = (
|
||||||
|
entity: DataTreeEntity,
|
||||||
|
propertyPath: string,
|
||||||
|
entityConfig: DataTreeEntityConfig,
|
||||||
|
) => {
|
||||||
|
if (!isJSAction(entity)) return false;
|
||||||
|
|
||||||
|
const { actionNames } = entityConfig as JSActionEntityConfig;
|
||||||
|
|
||||||
|
return actionNames.has(propertyPath);
|
||||||
|
};
|
||||||
|
|
||||||
export const convertMicroDiffToDeepDiff = (
|
export const convertMicroDiffToDeepDiff = (
|
||||||
microDiffDifferences: Difference[],
|
microDiffDifferences: Difference[],
|
||||||
): Diff<unknown, unknown>[] =>
|
): Diff<unknown, unknown>[] =>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import {
|
||||||
waitForSegmentInit,
|
waitForSegmentInit,
|
||||||
waitForFetchUserSuccess,
|
waitForFetchUserSuccess,
|
||||||
} from "ee/sagas/userSagas";
|
} from "ee/sagas/userSagas";
|
||||||
import { waitForFetchEnvironments } from "ee/sagas/EnvironmentSagas";
|
|
||||||
import { fetchJSCollectionsForView } from "actions/jsActionActions";
|
import { fetchJSCollectionsForView } from "actions/jsActionActions";
|
||||||
import {
|
import {
|
||||||
fetchAppThemesAction,
|
fetchAppThemesAction,
|
||||||
|
|
@ -154,14 +153,6 @@ export default class AppViewerEngine extends AppEngine {
|
||||||
yield call(waitForSegmentInit, true);
|
yield call(waitForSegmentInit, true);
|
||||||
endSpan(waitForSegmentSpan);
|
endSpan(waitForSegmentSpan);
|
||||||
|
|
||||||
const waitForEnvironmentsSpan = startNestedSpan(
|
|
||||||
"AppViewerEngine.waitForFetchEnvironments",
|
|
||||||
rootSpan,
|
|
||||||
);
|
|
||||||
|
|
||||||
yield call(waitForFetchEnvironments);
|
|
||||||
endSpan(waitForEnvironmentsSpan);
|
|
||||||
|
|
||||||
yield put(fetchAllPageEntityCompletion([executePageLoadActions()]));
|
yield put(fetchAllPageEntityCompletion([executePageLoadActions()]));
|
||||||
|
|
||||||
endSpan(loadAppEntitiesSpan);
|
endSpan(loadAppEntitiesSpan);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
// This file must be executed as early as possible to ensure the preloads are triggered ASAP
|
// This file must be executed as early as possible to ensure the preloads are triggered ASAP
|
||||||
import "./preload-route-chunks";
|
import "./preload-route-chunks";
|
||||||
|
// Initialise eval worker instance
|
||||||
|
import "utils/workerInstances";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import "./wdyr";
|
import "./wdyr";
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,8 @@ import log from "loglevel";
|
||||||
import { EMPTY_RESPONSE } from "components/editorComponents/emptyResponse";
|
import { EMPTY_RESPONSE } from "components/editorComponents/emptyResponse";
|
||||||
import type { AppState } from "ee/reducers";
|
import type { AppState } from "ee/reducers";
|
||||||
import { DEFAULT_EXECUTE_ACTION_TIMEOUT_MS } from "ee/constants/ApiConstants";
|
import { DEFAULT_EXECUTE_ACTION_TIMEOUT_MS } from "ee/constants/ApiConstants";
|
||||||
import { evaluateActionBindings, evalWorker } from "sagas/EvaluationsSaga";
|
import { evaluateActionBindings } from "sagas/EvaluationsSaga";
|
||||||
|
import { evalWorker } from "utils/workerInstances";
|
||||||
import { isBlobUrl, parseBlobUrl } from "utils/AppsmithUtils";
|
import { isBlobUrl, parseBlobUrl } from "utils/AppsmithUtils";
|
||||||
import { getType, Types } from "utils/TypeHelpers";
|
import { getType, Types } from "utils/TypeHelpers";
|
||||||
import { matchPath } from "react-router";
|
import { matchPath } from "react-router";
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { showToastOnExecutionError } from "sagas/ActionExecution/errorUtils";
|
||||||
import { setUserCurrentGeoLocation } from "actions/browserRequestActions";
|
import { setUserCurrentGeoLocation } from "actions/browserRequestActions";
|
||||||
import type { Channel } from "redux-saga";
|
import type { Channel } from "redux-saga";
|
||||||
import { channel } from "redux-saga";
|
import { channel } from "redux-saga";
|
||||||
import { evalWorker } from "sagas/EvaluationsSaga";
|
import { evalWorker } from "utils/workerInstances";
|
||||||
import type {
|
import type {
|
||||||
TGetGeoLocationDescription,
|
TGetGeoLocationDescription,
|
||||||
TWatchGeoLocationDescription,
|
TWatchGeoLocationDescription,
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ import type { TMessage } from "utils/MessageUtil";
|
||||||
import { MessageType } from "utils/MessageUtil";
|
import { MessageType } from "utils/MessageUtil";
|
||||||
import type { ResponsePayload } from "../sagas/EvaluationsSaga";
|
import type { ResponsePayload } from "../sagas/EvaluationsSaga";
|
||||||
import {
|
import {
|
||||||
evalWorker,
|
|
||||||
executeTriggerRequestSaga,
|
executeTriggerRequestSaga,
|
||||||
updateDataTreeHandler,
|
updateDataTreeHandler,
|
||||||
} from "../sagas/EvaluationsSaga";
|
} from "../sagas/EvaluationsSaga";
|
||||||
|
import { evalWorker } from "utils/workerInstances";
|
||||||
import { handleStoreOperations } from "./ActionExecution/StoreActionSaga";
|
import { handleStoreOperations } from "./ActionExecution/StoreActionSaga";
|
||||||
import type { EvalTreeResponseData } from "workers/Evaluation/types";
|
import type { EvalTreeResponseData } from "workers/Evaluation/types";
|
||||||
import isEmpty from "lodash/isEmpty";
|
import isEmpty from "lodash/isEmpty";
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import {
|
||||||
defaultAffectedJSObjects,
|
defaultAffectedJSObjects,
|
||||||
evalQueueBuffer,
|
evalQueueBuffer,
|
||||||
evaluateTreeSaga,
|
evaluateTreeSaga,
|
||||||
evalWorker,
|
|
||||||
} from "./EvaluationsSaga";
|
} from "./EvaluationsSaga";
|
||||||
|
import { evalWorker } from "utils/workerInstances";
|
||||||
import { expectSaga } from "redux-saga-test-plan";
|
import { expectSaga } from "redux-saga-test-plan";
|
||||||
import { EVAL_WORKER_ACTIONS } from "ee/workers/Evaluation/evalWorkerActions";
|
import { EVAL_WORKER_ACTIONS } from "ee/workers/Evaluation/evalWorkerActions";
|
||||||
import { select } from "redux-saga/effects";
|
import { select } from "redux-saga/effects";
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import {
|
||||||
import { getMetaWidgets, getWidgets, getWidgetsMeta } from "sagas/selectors";
|
import { getMetaWidgets, getWidgets, getWidgetsMeta } from "sagas/selectors";
|
||||||
import type { WidgetTypeConfigMap } from "WidgetProvider/factory";
|
import type { WidgetTypeConfigMap } from "WidgetProvider/factory";
|
||||||
import WidgetFactory from "WidgetProvider/factory";
|
import WidgetFactory from "WidgetProvider/factory";
|
||||||
import { GracefulWorkerService } from "utils/WorkerUtil";
|
import { evalWorker } from "utils/workerInstances";
|
||||||
import type { EvalError, EvaluationError } from "utils/DynamicBindingUtils";
|
import type { EvalError, EvaluationError } from "utils/DynamicBindingUtils";
|
||||||
import { PropertyEvaluationErrorType } from "utils/DynamicBindingUtils";
|
import { PropertyEvaluationErrorType } from "utils/DynamicBindingUtils";
|
||||||
import { EVAL_WORKER_ACTIONS } from "ee/workers/Evaluation/evalWorkerActions";
|
import { EVAL_WORKER_ACTIONS } from "ee/workers/Evaluation/evalWorkerActions";
|
||||||
|
|
@ -120,18 +120,6 @@ import { getInstanceId } from "ee/selectors/tenantSelectors";
|
||||||
|
|
||||||
const APPSMITH_CONFIGS = getAppsmithConfigs();
|
const APPSMITH_CONFIGS = getAppsmithConfigs();
|
||||||
|
|
||||||
export const evalWorker = new GracefulWorkerService(
|
|
||||||
new Worker(
|
|
||||||
new URL("../workers/Evaluation/evaluation.worker.ts", import.meta.url),
|
|
||||||
{
|
|
||||||
type: "module",
|
|
||||||
// Note: the `Worker` part of the name is slightly important – LinkRelPreload_spec.js
|
|
||||||
// relies on it to find workers in the list of all requests.
|
|
||||||
name: "evalWorker",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let widgetTypeConfigMap: WidgetTypeConfigMap;
|
let widgetTypeConfigMap: WidgetTypeConfigMap;
|
||||||
|
|
||||||
export function* updateDataTreeHandler(
|
export function* updateDataTreeHandler(
|
||||||
|
|
@ -902,5 +890,3 @@ export default function* evaluationSagaListeners() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { evalWorker as EvalWorker };
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import { getCurrentApplicationId } from "selectors/editorSelectors";
|
||||||
import CodemirrorTernService from "utils/autocomplete/CodemirrorTernService";
|
import CodemirrorTernService from "utils/autocomplete/CodemirrorTernService";
|
||||||
import { EVAL_WORKER_ACTIONS } from "ee/workers/Evaluation/evalWorkerActions";
|
import { EVAL_WORKER_ACTIONS } from "ee/workers/Evaluation/evalWorkerActions";
|
||||||
import { validateResponse } from "./ErrorSagas";
|
import { validateResponse } from "./ErrorSagas";
|
||||||
import { EvalWorker } from "./EvaluationsSaga";
|
import { evalWorker as EvalWorker } from "utils/workerInstances";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import { APP_MODE } from "entities/App";
|
import { APP_MODE } from "entities/App";
|
||||||
import { getAppMode } from "ee/selectors/applicationSelectors";
|
import { getAppMode } from "ee/selectors/applicationSelectors";
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import type { EvalTreeResponseData } from "workers/Evaluation/types";
|
||||||
import { endSpan, startRootSpan } from "UITelemetry/generateTraces";
|
import { endSpan, startRootSpan } from "UITelemetry/generateTraces";
|
||||||
import { getJSActionPathNameToDisplay } from "ee/utils/actionExecutionUtils";
|
import { getJSActionPathNameToDisplay } from "ee/utils/actionExecutionUtils";
|
||||||
import { showToastOnExecutionError } from "./ActionExecution/errorUtils";
|
import { showToastOnExecutionError } from "./ActionExecution/errorUtils";
|
||||||
|
import { waitForFetchEnvironments } from "ee/sagas/EnvironmentSagas";
|
||||||
|
|
||||||
let successfulBindingsMap: SuccessfulBindingMap | undefined;
|
let successfulBindingsMap: SuccessfulBindingMap | undefined;
|
||||||
|
|
||||||
|
|
@ -190,6 +191,9 @@ export function* logSuccessfulBindings(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* postEvalActionDispatcher(actions: Array<AnyReduxAction>) {
|
export function* postEvalActionDispatcher(actions: Array<AnyReduxAction>) {
|
||||||
|
// Wait for environments api fetch before dispatching actions
|
||||||
|
yield call(waitForFetchEnvironments);
|
||||||
|
|
||||||
for (const action of actions) {
|
for (const action of actions) {
|
||||||
yield put(action);
|
yield put(action);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
app/client/src/utils/workerInstances.ts
Normal file
13
app/client/src/utils/workerInstances.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { GracefulWorkerService } from "./WorkerUtil";
|
||||||
|
|
||||||
|
export const evalWorker = new GracefulWorkerService(
|
||||||
|
new Worker(
|
||||||
|
new URL("../workers/Evaluation/evaluation.worker.ts", import.meta.url),
|
||||||
|
{
|
||||||
|
type: "module",
|
||||||
|
// Note: the `Worker` part of the name is slightly important – LinkRelPreload_spec.js
|
||||||
|
// relies on it to find workers in the list of all requests.
|
||||||
|
name: "evalWorker",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
@ -5,7 +5,7 @@ import { EvalErrorTypes, getEvalValuePath } from "utils/DynamicBindingUtils";
|
||||||
import type { JSUpdate, ParsedJSSubAction } from "utils/JSPaneUtils";
|
import type { JSUpdate, ParsedJSSubAction } from "utils/JSPaneUtils";
|
||||||
import { parseJSObject, isJSFunctionProperty } from "@shared/ast";
|
import { parseJSObject, isJSFunctionProperty } from "@shared/ast";
|
||||||
import type DataTreeEvaluator from "workers/common/DataTreeEvaluator";
|
import type DataTreeEvaluator from "workers/common/DataTreeEvaluator";
|
||||||
import evaluateSync from "workers/Evaluation/evaluate";
|
import { evaluateSync } from "workers/Evaluation/evaluate";
|
||||||
import type { DataTreeDiff } from "ee/workers/Evaluation/evaluationUtils";
|
import type { DataTreeDiff } from "ee/workers/Evaluation/evaluationUtils";
|
||||||
import {
|
import {
|
||||||
DataTreeDiffEvent,
|
DataTreeDiffEvent,
|
||||||
|
|
|
||||||
|
|
@ -195,9 +195,6 @@ describe("saveResolvedFunctionsAndJSUpdates", function () {
|
||||||
{
|
{
|
||||||
key: "myFun2",
|
key: "myFun2",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: "myFun2",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
bindingPaths: {
|
bindingPaths: {
|
||||||
body: "SMART_SUBSTITUTE",
|
body: "SMART_SUBSTITUTE",
|
||||||
|
|
@ -216,6 +213,7 @@ describe("saveResolvedFunctionsAndJSUpdates", function () {
|
||||||
pluginType: "JS",
|
pluginType: "JS",
|
||||||
name: "JSObject1",
|
name: "JSObject1",
|
||||||
actionId: "64013546b956c26882acc587",
|
actionId: "64013546b956c26882acc587",
|
||||||
|
actionNames: new Set(["myFun1", "myFun2"]),
|
||||||
} as JSActionEntityConfig,
|
} as JSActionEntityConfig,
|
||||||
};
|
};
|
||||||
const entityName = "JSObject1";
|
const entityName = "JSObject1";
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import type { EvalContext } from "workers/Evaluation/evaluate";
|
||||||
import { createEvaluationContext } from "workers/Evaluation/evaluate";
|
import { createEvaluationContext } from "workers/Evaluation/evaluate";
|
||||||
import { MessageType } from "utils/MessageUtil";
|
import { MessageType } from "utils/MessageUtil";
|
||||||
import {
|
import {
|
||||||
addDataTreeToContext,
|
getDataTreeContext,
|
||||||
addPlatformFunctionsToEvalContext,
|
addPlatformFunctionsToEvalContext,
|
||||||
} from "ee/workers/Evaluation/Actions";
|
} from "ee/workers/Evaluation/Actions";
|
||||||
import TriggerEmitter, { BatchKey } from "../fns/utils/TriggerEmitter";
|
import TriggerEmitter, { BatchKey } from "../fns/utils/TriggerEmitter";
|
||||||
|
|
@ -548,12 +548,13 @@ describe("Test addDataTreeToContext method", () => {
|
||||||
const evalContext: EvalContext = {};
|
const evalContext: EvalContext = {};
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
addDataTreeToContext({
|
const EVAL_CONTEXT = getDataTreeContext({
|
||||||
EVAL_CONTEXT: evalContext,
|
|
||||||
dataTree: dataTree as unknown as DataTree,
|
dataTree: dataTree as unknown as DataTree,
|
||||||
configTree,
|
configTree,
|
||||||
isTriggerBased: true,
|
isTriggerBased: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.assign(evalContext, EVAL_CONTEXT);
|
||||||
addPlatformFunctionsToEvalContext(evalContext);
|
addPlatformFunctionsToEvalContext(evalContext);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import evaluate, {
|
import {
|
||||||
|
evaluateSync,
|
||||||
createEvaluationContext,
|
createEvaluationContext,
|
||||||
evaluateAsync,
|
evaluateAsync,
|
||||||
} from "workers/Evaluation/evaluate";
|
} from "workers/Evaluation/evaluate";
|
||||||
|
|
@ -54,29 +55,29 @@ describe("evaluateSync", () => {
|
||||||
});
|
});
|
||||||
it("unescapes string before evaluation", () => {
|
it("unescapes string before evaluation", () => {
|
||||||
const js = '\\"Hello!\\"';
|
const js = '\\"Hello!\\"';
|
||||||
const response = evaluate(js, {}, false);
|
const response = evaluateSync(js, {}, false);
|
||||||
|
|
||||||
expect(response.result).toBe("Hello!");
|
expect(response.result).toBe("Hello!");
|
||||||
});
|
});
|
||||||
it("evaluate string post unescape in v1", () => {
|
it("evaluate string post unescape in v1", () => {
|
||||||
const js = '[1, 2, 3].join("\\\\n")';
|
const js = '[1, 2, 3].join("\\\\n")';
|
||||||
const response = evaluate(js, {}, false);
|
const response = evaluateSync(js, {}, false);
|
||||||
|
|
||||||
expect(response.result).toBe("1\n2\n3");
|
expect(response.result).toBe("1\n2\n3");
|
||||||
});
|
});
|
||||||
it("evaluate string without unescape in v2", () => {
|
it("evaluate string without unescape in v2", () => {
|
||||||
self.evaluationVersion = 2;
|
self.evaluationVersion = 2;
|
||||||
const js = '[1, 2, 3].join("\\n")';
|
const js = '[1, 2, 3].join("\\n")';
|
||||||
const response = evaluate(js, {}, false);
|
const response = evaluateSync(js, {}, false);
|
||||||
|
|
||||||
expect(response.result).toBe("1\n2\n3");
|
expect(response.result).toBe("1\n2\n3");
|
||||||
});
|
});
|
||||||
it("throws error for undefined js", () => {
|
it("throws error for undefined js", () => {
|
||||||
// @ts-expect-error: Types are not available
|
// @ts-expect-error: Types are not available
|
||||||
expect(() => evaluate(undefined, {})).toThrow(TypeError);
|
expect(() => evaluateSync(undefined, {})).toThrow(TypeError);
|
||||||
});
|
});
|
||||||
it("Returns for syntax errors", () => {
|
it("Returns for syntax errors", () => {
|
||||||
const response1 = evaluate("wrongJS", {}, false);
|
const response1 = evaluateSync("wrongJS", {}, false);
|
||||||
|
|
||||||
expect(response1).toStrictEqual({
|
expect(response1).toStrictEqual({
|
||||||
result: undefined,
|
result: undefined,
|
||||||
|
|
@ -100,7 +101,7 @@ describe("evaluateSync", () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const response2 = evaluate("{}.map()", {}, false);
|
const response2 = evaluateSync("{}.map()", {}, false);
|
||||||
|
|
||||||
expect(response2).toStrictEqual({
|
expect(response2).toStrictEqual({
|
||||||
result: undefined,
|
result: undefined,
|
||||||
|
|
@ -130,21 +131,21 @@ describe("evaluateSync", () => {
|
||||||
});
|
});
|
||||||
it("evaluates value from data tree", () => {
|
it("evaluates value from data tree", () => {
|
||||||
const js = "Input1.text";
|
const js = "Input1.text";
|
||||||
const response = evaluate(js, dataTree, false);
|
const response = evaluateSync(js, dataTree, false);
|
||||||
|
|
||||||
expect(response.result).toBe("value");
|
expect(response.result).toBe("value");
|
||||||
});
|
});
|
||||||
it("disallows unsafe function calls", () => {
|
it("disallows unsafe function calls", () => {
|
||||||
const js = "setImmediate(() => {}, 100)";
|
const js = "setImmediate(() => {}, 100)";
|
||||||
const response = evaluate(js, dataTree, false);
|
const response = evaluateSync(js, dataTree, false);
|
||||||
|
|
||||||
expect(response).toStrictEqual({
|
expect(response).toStrictEqual({
|
||||||
result: undefined,
|
result: undefined,
|
||||||
errors: [
|
errors: [
|
||||||
{
|
{
|
||||||
errorMessage: {
|
errorMessage: {
|
||||||
name: "ReferenceError",
|
name: "TypeError",
|
||||||
message: "setImmediate is not defined",
|
message: "setImmediate is not a function",
|
||||||
},
|
},
|
||||||
errorType: "PARSE",
|
errorType: "PARSE",
|
||||||
kind: {
|
kind: {
|
||||||
|
|
@ -166,51 +167,51 @@ describe("evaluateSync", () => {
|
||||||
});
|
});
|
||||||
it("has access to extra library functions", () => {
|
it("has access to extra library functions", () => {
|
||||||
const js = "_.add(1,2)";
|
const js = "_.add(1,2)";
|
||||||
const response = evaluate(js, dataTree, false);
|
const response = evaluateSync(js, dataTree, false);
|
||||||
|
|
||||||
expect(response.result).toBe(3);
|
expect(response.result).toBe(3);
|
||||||
});
|
});
|
||||||
it("evaluates functions with callback data", () => {
|
it("evaluates functions with callback data", () => {
|
||||||
const js = "(arg1, arg2) => arg1.value + arg2";
|
const js = "(arg1, arg2) => arg1.value + arg2";
|
||||||
const callbackData = [{ value: "test" }, "1"];
|
const callbackData = [{ value: "test" }, "1"];
|
||||||
const response = evaluate(js, dataTree, false, {}, callbackData);
|
const response = evaluateSync(js, dataTree, false, {}, callbackData);
|
||||||
|
|
||||||
expect(response.result).toBe("test1");
|
expect(response.result).toBe("test1");
|
||||||
});
|
});
|
||||||
it("handles EXPRESSIONS with new lines", () => {
|
it("handles EXPRESSIONS with new lines", () => {
|
||||||
let js = "\n";
|
let js = "\n";
|
||||||
let response = evaluate(js, dataTree, false);
|
let response = evaluateSync(js, dataTree, false);
|
||||||
|
|
||||||
expect(response.errors.length).toBe(0);
|
expect(response.errors.length).toBe(0);
|
||||||
|
|
||||||
js = "\n\n\n";
|
js = "\n\n\n";
|
||||||
response = evaluate(js, dataTree, false);
|
response = evaluateSync(js, dataTree, false);
|
||||||
expect(response.errors.length).toBe(0);
|
expect(response.errors.length).toBe(0);
|
||||||
});
|
});
|
||||||
it("handles TRIGGERS with new lines", () => {
|
it("handles TRIGGERS with new lines", () => {
|
||||||
let js = "\n";
|
let js = "\n";
|
||||||
let response = evaluate(js, dataTree, false, undefined, undefined);
|
let response = evaluateSync(js, dataTree, false, undefined, undefined);
|
||||||
|
|
||||||
expect(response.errors.length).toBe(0);
|
expect(response.errors.length).toBe(0);
|
||||||
|
|
||||||
js = "\n\n\n";
|
js = "\n\n\n";
|
||||||
response = evaluate(js, dataTree, false, undefined, undefined);
|
response = evaluateSync(js, dataTree, false, undefined, undefined);
|
||||||
expect(response.errors.length).toBe(0);
|
expect(response.errors.length).toBe(0);
|
||||||
});
|
});
|
||||||
it("handles ANONYMOUS_FUNCTION with new lines", () => {
|
it("handles ANONYMOUS_FUNCTION with new lines", () => {
|
||||||
let js = "\n";
|
let js = "\n";
|
||||||
let response = evaluate(js, dataTree, false, undefined, undefined);
|
let response = evaluateSync(js, dataTree, false, undefined, undefined);
|
||||||
|
|
||||||
expect(response.errors.length).toBe(0);
|
expect(response.errors.length).toBe(0);
|
||||||
|
|
||||||
js = "\n\n\n";
|
js = "\n\n\n";
|
||||||
response = evaluate(js, dataTree, false, undefined, undefined);
|
response = evaluateSync(js, dataTree, false, undefined, undefined);
|
||||||
expect(response.errors.length).toBe(0);
|
expect(response.errors.length).toBe(0);
|
||||||
});
|
});
|
||||||
it("has access to this context", () => {
|
it("has access to this context", () => {
|
||||||
const js = "this.contextVariable";
|
const js = "this.contextVariable";
|
||||||
const thisContext = { contextVariable: "test" };
|
const thisContext = { contextVariable: "test" };
|
||||||
const response = evaluate(js, dataTree, false, { thisContext });
|
const response = evaluateSync(js, dataTree, false, { thisContext });
|
||||||
|
|
||||||
expect(response.result).toBe("test");
|
expect(response.result).toBe("test");
|
||||||
// there should not be any error when accessing "this" variables
|
// there should not be any error when accessing "this" variables
|
||||||
|
|
@ -220,7 +221,7 @@ describe("evaluateSync", () => {
|
||||||
it("has access to additional global context", () => {
|
it("has access to additional global context", () => {
|
||||||
const js = "contextVariable";
|
const js = "contextVariable";
|
||||||
const globalContext = { contextVariable: "test" };
|
const globalContext = { contextVariable: "test" };
|
||||||
const response = evaluate(js, dataTree, false, { globalContext });
|
const response = evaluateSync(js, dataTree, false, { globalContext });
|
||||||
|
|
||||||
expect(response.result).toBe("test");
|
expect(response.result).toBe("test");
|
||||||
expect(response.errors).toHaveLength(0);
|
expect(response.errors).toHaveLength(0);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import WidgetFactory from "WidgetProvider/factory";
|
||||||
import { generateDataTreeWidget } from "entities/DataTree/dataTreeWidget";
|
import { generateDataTreeWidget } from "entities/DataTree/dataTreeWidget";
|
||||||
import { sortObjectWithArray } from "../../../utils/treeUtils";
|
import { sortObjectWithArray } from "../../../utils/treeUtils";
|
||||||
import klona from "klona";
|
import klona from "klona";
|
||||||
|
|
||||||
import { APP_MODE } from "entities/App";
|
import { APP_MODE } from "entities/App";
|
||||||
|
|
||||||
const klonaFullSpy = jest.fn();
|
const klonaFullSpy = jest.fn();
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import {
|
||||||
PrimitiveErrorModifier,
|
PrimitiveErrorModifier,
|
||||||
TypeErrorModifier,
|
TypeErrorModifier,
|
||||||
} from "./errorModifier";
|
} from "./errorModifier";
|
||||||
import { addDataTreeToContext } from "ee/workers/Evaluation/Actions";
|
import { getDataTreeContext } from "ee/workers/Evaluation/Actions";
|
||||||
import { set } from "lodash";
|
import { set } from "lodash";
|
||||||
import { klona } from "klona";
|
import { klona } from "klona";
|
||||||
import { getEntityNameAndPropertyPath } from "ee/workers/Evaluation/evaluationUtils";
|
import { getEntityNameAndPropertyPath } from "ee/workers/Evaluation/evaluationUtils";
|
||||||
|
|
@ -103,7 +103,7 @@ const ignoreGlobalObjectKeys = new Set([
|
||||||
"location",
|
"location",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function resetWorkerGlobalScope() {
|
export function resetWorkerGlobalScope() {
|
||||||
const jsLibraryAccessorSet = JSLibraryAccessor.getSet();
|
const jsLibraryAccessorSet = JSLibraryAccessor.getSet();
|
||||||
|
|
||||||
for (const key of Object.keys(self)) {
|
for (const key of Object.keys(self)) {
|
||||||
|
|
@ -273,14 +273,15 @@ export const createEvaluationContext = (args: createEvaluationContextArgs) => {
|
||||||
Object.assign(EVAL_CONTEXT, context.globalContext);
|
Object.assign(EVAL_CONTEXT, context.globalContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
addDataTreeToContext({
|
const dataTreeContext = getDataTreeContext({
|
||||||
EVAL_CONTEXT,
|
|
||||||
dataTree,
|
dataTree,
|
||||||
configTree,
|
configTree,
|
||||||
removeEntityFunctions: !!removeEntityFunctions,
|
removeEntityFunctions: !!removeEntityFunctions,
|
||||||
isTriggerBased,
|
isTriggerBased,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.assign(EVAL_CONTEXT, dataTreeContext);
|
||||||
|
|
||||||
overrideEvalContext(EVAL_CONTEXT, context?.overrideContext);
|
overrideEvalContext(EVAL_CONTEXT, context?.overrideContext);
|
||||||
|
|
||||||
return EVAL_CONTEXT;
|
return EVAL_CONTEXT;
|
||||||
|
|
@ -365,7 +366,7 @@ export function setEvalContext({
|
||||||
Object.assign(self, evalContext);
|
Object.assign(self, evalContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function evaluateSync(
|
export function evaluateSync(
|
||||||
userScript: string,
|
userScript: string,
|
||||||
dataTree: DataTree,
|
dataTree: DataTree,
|
||||||
isJSCollection: boolean,
|
isJSCollection: boolean,
|
||||||
|
|
@ -373,7 +374,8 @@ export default function evaluateSync(
|
||||||
// TODO: Fix this the next time the file is edited
|
// TODO: Fix this the next time the file is edited
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
evalArguments?: Array<any>,
|
evalArguments?: Array<any>,
|
||||||
configTree?: ConfigTree,
|
configTree: ConfigTree = {},
|
||||||
|
evalContextCache?: EvalContext,
|
||||||
): EvalResult {
|
): EvalResult {
|
||||||
return (function () {
|
return (function () {
|
||||||
const errors: EvaluationError[] = [];
|
const errors: EvaluationError[] = [];
|
||||||
|
|
@ -394,17 +396,35 @@ export default function evaluateSync(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
resetWorkerGlobalScope();
|
self["$isDataField"] = true;
|
||||||
|
const EVAL_CONTEXT: EvalContext = {};
|
||||||
|
|
||||||
setEvalContext({
|
///// Adding callback data
|
||||||
|
EVAL_CONTEXT.ARGUMENTS = evalArguments;
|
||||||
|
//// Adding contextual data not part of data tree
|
||||||
|
EVAL_CONTEXT.THIS_CONTEXT = context?.thisContext || {};
|
||||||
|
|
||||||
|
if (context?.globalContext) {
|
||||||
|
Object.assign(EVAL_CONTEXT, context.globalContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evalContextCache) {
|
||||||
|
Object.assign(EVAL_CONTEXT, evalContextCache);
|
||||||
|
} else {
|
||||||
|
const dataTreeContext = getDataTreeContext({
|
||||||
dataTree,
|
dataTree,
|
||||||
configTree,
|
configTree,
|
||||||
isDataField: true,
|
removeEntityFunctions: false,
|
||||||
isTriggerBased: isJSCollection,
|
isTriggerBased: isJSCollection,
|
||||||
context,
|
|
||||||
evalArguments,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.assign(EVAL_CONTEXT, dataTreeContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
overrideEvalContext(EVAL_CONTEXT, context?.overrideContext);
|
||||||
|
|
||||||
|
Object.assign(self, EVAL_CONTEXT);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = indirectEval(script);
|
result = indirectEval(script);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import type {
|
||||||
import { isWidget } from "ee/workers/Evaluation/evaluationUtils";
|
import { isWidget } from "ee/workers/Evaluation/evaluationUtils";
|
||||||
import { klona } from "klona";
|
import { klona } from "klona";
|
||||||
import { getDynamicBindings, isDynamicValue } from "utils/DynamicBindingUtils";
|
import { getDynamicBindings, isDynamicValue } from "utils/DynamicBindingUtils";
|
||||||
import evaluateSync, { setEvalContext } from "../evaluate";
|
import { evaluateSync, setEvalContext } from "../evaluate";
|
||||||
import type { DescendantWidgetMap } from "sagas/WidgetOperationUtils";
|
import type { DescendantWidgetMap } from "sagas/WidgetOperationUtils";
|
||||||
import type { MetaState } from "reducers/entityReducers/metaReducer";
|
import type { MetaState } from "reducers/entityReducers/metaReducer";
|
||||||
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { createMessage, customJSLibraryMessages } from "ee/constants/messages";
|
|
||||||
import difference from "lodash/difference";
|
import difference from "lodash/difference";
|
||||||
import type { Def } from "tern";
|
import type { Def } from "tern";
|
||||||
import { invalidEntityIdentifiers } from "workers/common/DependencyMap/utils";
|
import { invalidEntityIdentifiers } from "workers/common/DependencyMap/utils";
|
||||||
|
|
@ -34,7 +33,7 @@ enum LibraryInstallError {
|
||||||
class ImportError extends Error {
|
class ImportError extends Error {
|
||||||
code = LibraryInstallError.ImportError;
|
code = LibraryInstallError.ImportError;
|
||||||
constructor(url: string) {
|
constructor(url: string) {
|
||||||
super(createMessage(customJSLibraryMessages.IMPORT_URL_ERROR, url));
|
super(`The script at ${url} cannot be installed.`);
|
||||||
this.name = "ImportError";
|
this.name = "ImportError";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -42,7 +41,7 @@ class ImportError extends Error {
|
||||||
class TernDefinitionError extends Error {
|
class TernDefinitionError extends Error {
|
||||||
code = LibraryInstallError.TernDefinitionError;
|
code = LibraryInstallError.TernDefinitionError;
|
||||||
constructor(name: string) {
|
constructor(name: string) {
|
||||||
super(createMessage(customJSLibraryMessages.DEFS_FAILED_ERROR, name));
|
super(`Failed to generate autocomplete definitions for ${name}.`);
|
||||||
this.name = "TernDefinitionError";
|
this.name = "TernDefinitionError";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import type { FeatureFlags } from "ee/entities/FeatureFlag";
|
import type { FeatureFlags } from "ee/entities/FeatureFlag";
|
||||||
|
|
||||||
export class WorkerEnv {
|
export class WorkerEnv {
|
||||||
// TODO: Fix this the next time the file is edited
|
static flags: FeatureFlags = {} as FeatureFlags;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
static flags: any;
|
|
||||||
static cloudHosting: boolean;
|
static cloudHosting: boolean;
|
||||||
|
|
||||||
static setFeatureFlags(featureFlags: FeatureFlags) {
|
static setFeatureFlags(featureFlags: FeatureFlags) {
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import type { DataTreeDiff } from "ee/workers/Evaluation/evaluationUtils";
|
||||||
import {
|
import {
|
||||||
convertMicroDiffToDeepDiff,
|
convertMicroDiffToDeepDiff,
|
||||||
getAllPathsBasedOnDiffPaths,
|
getAllPathsBasedOnDiffPaths,
|
||||||
|
isPropertyAnEntityAction,
|
||||||
} from "ee/workers/Evaluation/evaluationUtils";
|
} from "ee/workers/Evaluation/evaluationUtils";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -86,8 +87,11 @@ import {
|
||||||
EXECUTION_PARAM_REFERENCE_REGEX,
|
EXECUTION_PARAM_REFERENCE_REGEX,
|
||||||
THIS_DOT_PARAMS_KEY,
|
THIS_DOT_PARAMS_KEY,
|
||||||
} from "constants/AppsmithActionConstants/ActionConstants";
|
} from "constants/AppsmithActionConstants/ActionConstants";
|
||||||
import type { EvalResult, EvaluateContext } from "workers/Evaluation/evaluate";
|
import {
|
||||||
import evaluateSync, {
|
evaluateSync,
|
||||||
|
resetWorkerGlobalScope,
|
||||||
|
type EvalResult,
|
||||||
|
type EvaluateContext,
|
||||||
evaluateAsync,
|
evaluateAsync,
|
||||||
setEvalContext,
|
setEvalContext,
|
||||||
} from "workers/Evaluation/evaluate";
|
} from "workers/Evaluation/evaluate";
|
||||||
|
|
@ -148,6 +152,8 @@ import {
|
||||||
EComputationCacheName,
|
EComputationCacheName,
|
||||||
type ICacheProps,
|
type ICacheProps,
|
||||||
} from "../AppComputationCache/types";
|
} from "../AppComputationCache/types";
|
||||||
|
import { getDataTreeContext } from "ee/workers/Evaluation/Actions";
|
||||||
|
import { WorkerEnv } from "workers/Evaluation/handlers/workerEnv";
|
||||||
|
|
||||||
type SortedDependencies = Array<string>;
|
type SortedDependencies = Array<string>;
|
||||||
export interface EvalProps {
|
export interface EvalProps {
|
||||||
|
|
@ -1059,6 +1065,8 @@ export default class DataTreeEvaluator {
|
||||||
staleMetaIds: string[];
|
staleMetaIds: string[];
|
||||||
contextTree: DataTree;
|
contextTree: DataTree;
|
||||||
} {
|
} {
|
||||||
|
resetWorkerGlobalScope();
|
||||||
|
|
||||||
const safeTree = klonaJSON(unEvalTree);
|
const safeTree = klonaJSON(unEvalTree);
|
||||||
const dataStore = DataStore.getDataStore();
|
const dataStore = DataStore.getDataStore();
|
||||||
const dataStoreClone = klonaJSON(dataStore);
|
const dataStoreClone = klonaJSON(dataStore);
|
||||||
|
|
@ -1084,6 +1092,16 @@ export default class DataTreeEvaluator {
|
||||||
const { isFirstTree, metaWidgets, unevalUpdates } = options;
|
const { isFirstTree, metaWidgets, unevalUpdates } = options;
|
||||||
let staleMetaIds: string[] = [];
|
let staleMetaIds: string[] = [];
|
||||||
|
|
||||||
|
let evalContextCache;
|
||||||
|
|
||||||
|
if (WorkerEnv.flags.release_evaluation_scope_cache) {
|
||||||
|
evalContextCache = getDataTreeContext({
|
||||||
|
dataTree: contextTree,
|
||||||
|
configTree: oldConfigTree,
|
||||||
|
isTriggerBased: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const fullPropertyPath of evaluationOrder) {
|
for (const fullPropertyPath of evaluationOrder) {
|
||||||
const { entityName, propertyPath } =
|
const { entityName, propertyPath } =
|
||||||
|
|
@ -1093,6 +1111,11 @@ export default class DataTreeEvaluator {
|
||||||
|
|
||||||
if (!isWidgetActionOrJsObject(entity)) continue;
|
if (!isWidgetActionOrJsObject(entity)) continue;
|
||||||
|
|
||||||
|
// Skip evaluations for actions in JSObjects
|
||||||
|
if (isPropertyAnEntityAction(entity, propertyPath, entityConfig)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Fix this the next time the file is edited
|
// TODO: Fix this the next time the file is edited
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
let unEvalPropertyValue = get(contextTree as any, fullPropertyPath);
|
let unEvalPropertyValue = get(contextTree as any, fullPropertyPath);
|
||||||
|
|
@ -1144,6 +1167,7 @@ export default class DataTreeEvaluator {
|
||||||
contextData,
|
contextData,
|
||||||
undefined,
|
undefined,
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
|
evalContextCache,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.errors.push({
|
this.errors.push({
|
||||||
|
|
@ -1209,6 +1233,13 @@ export default class DataTreeEvaluator {
|
||||||
set(contextTree, fullPropertyPath, parsedValue);
|
set(contextTree, fullPropertyPath, parsedValue);
|
||||||
set(safeTree, fullPropertyPath, klonaJSON(parsedValue));
|
set(safeTree, fullPropertyPath, klonaJSON(parsedValue));
|
||||||
|
|
||||||
|
if (
|
||||||
|
WorkerEnv.flags.release_evaluation_scope_cache &&
|
||||||
|
evalContextCache
|
||||||
|
) {
|
||||||
|
set(evalContextCache, fullPropertyPath, klonaJSON(parsedValue));
|
||||||
|
}
|
||||||
|
|
||||||
staleMetaIds = staleMetaIds.concat(
|
staleMetaIds = staleMetaIds.concat(
|
||||||
getStaleMetaStateIds({
|
getStaleMetaStateIds({
|
||||||
entity: widgetEntity,
|
entity: widgetEntity,
|
||||||
|
|
@ -1254,6 +1285,18 @@ export default class DataTreeEvaluator {
|
||||||
|
|
||||||
set(contextTree, fullPropertyPath, evalPropertyValue);
|
set(contextTree, fullPropertyPath, evalPropertyValue);
|
||||||
set(safeTree, fullPropertyPath, klonaJSON(evalPropertyValue));
|
set(safeTree, fullPropertyPath, klonaJSON(evalPropertyValue));
|
||||||
|
|
||||||
|
if (
|
||||||
|
WorkerEnv.flags.release_evaluation_scope_cache &&
|
||||||
|
evalContextCache
|
||||||
|
) {
|
||||||
|
set(
|
||||||
|
evalContextCache,
|
||||||
|
fullPropertyPath,
|
||||||
|
klonaJSON(evalPropertyValue),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ENTITY_TYPE.JSACTION: {
|
case ENTITY_TYPE.JSACTION: {
|
||||||
|
|
@ -1294,6 +1337,18 @@ export default class DataTreeEvaluator {
|
||||||
|
|
||||||
set(contextTree, fullPropertyPath, evalValue);
|
set(contextTree, fullPropertyPath, evalValue);
|
||||||
set(safeTree, fullPropertyPath, valueForSafeTree);
|
set(safeTree, fullPropertyPath, valueForSafeTree);
|
||||||
|
|
||||||
|
if (
|
||||||
|
WorkerEnv.flags.release_evaluation_scope_cache &&
|
||||||
|
evalContextCache
|
||||||
|
) {
|
||||||
|
set(
|
||||||
|
evalContextCache,
|
||||||
|
fullPropertyPath,
|
||||||
|
klonaJSON(evalPropertyValue),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
JSObjectCollection.setVariableValue(evalValue, fullPropertyPath);
|
JSObjectCollection.setVariableValue(evalValue, fullPropertyPath);
|
||||||
JSObjectCollection.setPrevUnEvalState({
|
JSObjectCollection.setPrevUnEvalState({
|
||||||
fullPath: fullPropertyPath,
|
fullPath: fullPropertyPath,
|
||||||
|
|
@ -1306,6 +1361,17 @@ export default class DataTreeEvaluator {
|
||||||
default:
|
default:
|
||||||
set(contextTree, fullPropertyPath, evalPropertyValue);
|
set(contextTree, fullPropertyPath, evalPropertyValue);
|
||||||
set(safeTree, fullPropertyPath, klonaJSON(evalPropertyValue));
|
set(safeTree, fullPropertyPath, klonaJSON(evalPropertyValue));
|
||||||
|
|
||||||
|
if (
|
||||||
|
WorkerEnv.flags.release_evaluation_scope_cache &&
|
||||||
|
evalContextCache
|
||||||
|
) {
|
||||||
|
set(
|
||||||
|
evalContextCache,
|
||||||
|
fullPropertyPath,
|
||||||
|
klonaJSON(evalPropertyValue),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -1417,6 +1483,7 @@ export default class DataTreeEvaluator {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
callBackData?: Array<any>,
|
callBackData?: Array<any>,
|
||||||
fullPropertyPath?: string,
|
fullPropertyPath?: string,
|
||||||
|
evalContextCache?: EvaluateContext,
|
||||||
) {
|
) {
|
||||||
// Get the {{binding}} bound values
|
// Get the {{binding}} bound values
|
||||||
let entity: DataTreeEntity | undefined = undefined;
|
let entity: DataTreeEntity | undefined = undefined;
|
||||||
|
|
@ -1467,6 +1534,7 @@ export default class DataTreeEvaluator {
|
||||||
!!entity && isAnyJSAction(entity),
|
!!entity && isAnyJSAction(entity),
|
||||||
contextData,
|
contextData,
|
||||||
callBackData,
|
callBackData,
|
||||||
|
evalContextCache,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (fullPropertyPath && evalErrors.length) {
|
if (fullPropertyPath && evalErrors.length) {
|
||||||
|
|
@ -1560,6 +1628,7 @@ export default class DataTreeEvaluator {
|
||||||
// TODO: Fix this the next time the file is edited
|
// TODO: Fix this the next time the file is edited
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
callbackData?: Array<any>,
|
callbackData?: Array<any>,
|
||||||
|
evalContextCache?: EvaluateContext,
|
||||||
): EvalResult {
|
): EvalResult {
|
||||||
let evalResponse: EvalResult;
|
let evalResponse: EvalResult;
|
||||||
|
|
||||||
|
|
@ -1574,6 +1643,8 @@ export default class DataTreeEvaluator {
|
||||||
isJSObject,
|
isJSObject,
|
||||||
contextData,
|
contextData,
|
||||||
callbackData,
|
callbackData,
|
||||||
|
{},
|
||||||
|
evalContextCache,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
evalResponse = {
|
evalResponse = {
|
||||||
|
|
|
||||||
|
|
@ -768,6 +768,7 @@ describe("isDataField", () => {
|
||||||
dependencyMap: {
|
dependencyMap: {
|
||||||
body: ["myFun2", "myFun1"],
|
body: ["myFun2", "myFun1"],
|
||||||
},
|
},
|
||||||
|
actionNames: new Set(["myFun1", "myFun2"]),
|
||||||
},
|
},
|
||||||
JSObject2: {
|
JSObject2: {
|
||||||
actionId: "644242aeadc0936a9b0e71cc",
|
actionId: "644242aeadc0936a9b0e71cc",
|
||||||
|
|
@ -821,6 +822,7 @@ describe("isDataField", () => {
|
||||||
dependencyMap: {
|
dependencyMap: {
|
||||||
body: ["myFun2", "myFun1"],
|
body: ["myFun2", "myFun1"],
|
||||||
},
|
},
|
||||||
|
actionNames: new Set(["myFun1", "myFun2"]),
|
||||||
},
|
},
|
||||||
MainContainer: {
|
MainContainer: {
|
||||||
defaultProps: {},
|
defaultProps: {},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user