@nocobase/flow-engine 2.1.0-alpha.1 → 2.1.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -661
- package/README.md +79 -10
- package/lib/BlockScopedFlowEngine.js +0 -1
- package/lib/FlowDefinition.d.ts +2 -0
- package/lib/JSRunner.d.ts +15 -0
- package/lib/JSRunner.js +82 -7
- package/lib/ViewScopedFlowEngine.js +8 -1
- package/lib/acl/Acl.js +13 -3
- package/lib/components/FlowContextSelector.js +155 -10
- package/lib/components/MobilePopup.js +6 -5
- package/lib/components/dnd/gridDragPlanner.d.ts +1 -0
- package/lib/components/dnd/gridDragPlanner.js +59 -3
- package/lib/components/settings/wrappers/component/SwitchWithTitle.js +2 -1
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +76 -15
- package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +24 -4
- package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +21 -3
- package/lib/components/subModel/AddSubModelButton.js +16 -1
- package/lib/components/subModel/utils.js +2 -2
- package/lib/components/variables/VariableInput.js +9 -4
- package/lib/components/variables/VariableTag.js +46 -39
- package/lib/components/variables/utils.d.ts +7 -0
- package/lib/components/variables/utils.js +42 -2
- package/lib/data-source/index.d.ts +7 -27
- package/lib/data-source/index.js +84 -51
- package/lib/executor/FlowExecutor.d.ts +2 -1
- package/lib/executor/FlowExecutor.js +190 -26
- package/lib/flowContext.d.ts +230 -7
- package/lib/flowContext.js +2270 -148
- package/lib/flowEngine.d.ts +160 -1
- package/lib/flowEngine.js +383 -26
- package/lib/flowI18n.js +6 -4
- package/lib/flowSettings.d.ts +14 -6
- package/lib/flowSettings.js +51 -17
- package/lib/index.d.ts +7 -1
- package/lib/index.js +21 -0
- package/lib/lazy-helper.d.ts +14 -0
- package/lib/lazy-helper.js +71 -0
- package/lib/locale/en-US.json +9 -2
- package/lib/locale/index.d.ts +14 -0
- package/lib/locale/zh-CN.json +8 -1
- package/lib/models/CollectionFieldModel.d.ts +1 -0
- package/lib/models/CollectionFieldModel.js +3 -2
- package/lib/models/flowModel.d.ts +7 -0
- package/lib/models/flowModel.js +83 -8
- package/lib/provider.js +7 -6
- package/lib/resources/baseRecordResource.d.ts +5 -0
- package/lib/resources/baseRecordResource.js +24 -0
- package/lib/resources/multiRecordResource.d.ts +1 -0
- package/lib/resources/multiRecordResource.js +11 -4
- package/lib/resources/singleRecordResource.js +2 -0
- package/lib/resources/sqlResource.d.ts +4 -3
- package/lib/resources/sqlResource.js +8 -3
- package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +12 -2
- package/lib/runjs-context/contexts/JSBlockRunJSContext.js +2 -2
- package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.d.ts +16 -0
- package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +125 -0
- package/lib/runjs-context/contexts/JSItemRunJSContext.js +12 -2
- package/lib/runjs-context/contexts/base.js +706 -41
- package/lib/runjs-context/contributions.d.ts +33 -0
- package/lib/runjs-context/contributions.js +88 -0
- package/lib/runjs-context/helpers.js +12 -1
- package/lib/runjs-context/registry.d.ts +1 -1
- package/lib/runjs-context/setup.js +22 -9
- package/lib/runjs-context/snippets/global/api-request.snippet.js +3 -3
- package/lib/runjs-context/snippets/global/import-esm.snippet.js +2 -3
- package/lib/runjs-context/snippets/global/query-selector.snippet.js +8 -3
- package/lib/runjs-context/snippets/global/require-amd.snippet.js +1 -1
- package/lib/runjs-context/snippets/index.d.ts +11 -1
- package/lib/runjs-context/snippets/index.js +61 -40
- package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.js +10 -7
- package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/echarts-init.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/render-react.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +5 -5
- package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +6 -6
- package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +3 -4
- package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.js +20 -3
- package/lib/runjs-context/snippets/scene/detail/format-number.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/form/render-basic.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +6 -3
- package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +3 -1
- package/lib/runjsLibs.d.ts +28 -0
- package/lib/runjsLibs.js +532 -0
- package/lib/scheduler/ModelOperationScheduler.d.ts +7 -1
- package/lib/scheduler/ModelOperationScheduler.js +28 -23
- package/lib/types.d.ts +63 -1
- package/lib/utils/associationObjectVariable.d.ts +2 -2
- package/lib/utils/createCollectionContextMeta.js +1 -0
- package/lib/utils/createEphemeralContext.js +2 -2
- package/lib/utils/dateVariable.d.ts +16 -0
- package/lib/utils/dateVariable.js +380 -0
- package/lib/utils/exceptions.d.ts +7 -0
- package/lib/utils/exceptions.js +10 -0
- package/lib/utils/index.d.ts +8 -3
- package/lib/utils/index.js +49 -0
- package/lib/utils/params-resolvers.js +16 -9
- package/lib/utils/parsePathnameToViewParams.js +1 -1
- package/lib/utils/resolveModuleUrl.d.ts +58 -0
- package/lib/utils/resolveModuleUrl.js +65 -0
- package/lib/utils/resolveRunJSObjectValues.d.ts +16 -0
- package/lib/utils/resolveRunJSObjectValues.js +61 -0
- package/lib/utils/runjsModuleLoader.d.ts +58 -0
- package/lib/utils/runjsModuleLoader.js +422 -0
- package/lib/utils/runjsTemplateCompat.d.ts +35 -0
- package/lib/utils/runjsTemplateCompat.js +743 -0
- package/lib/utils/runjsValue.d.ts +29 -0
- package/lib/utils/runjsValue.js +275 -0
- package/lib/utils/safeGlobals.d.ts +18 -8
- package/lib/utils/safeGlobals.js +164 -17
- package/lib/utils/schema-utils.d.ts +17 -1
- package/lib/utils/schema-utils.js +80 -0
- package/lib/views/FlowView.d.ts +7 -1
- package/lib/views/createViewMeta.d.ts +0 -7
- package/lib/views/createViewMeta.js +19 -70
- package/lib/views/index.d.ts +1 -2
- package/lib/views/index.js +4 -3
- package/lib/views/runViewBeforeClose.d.ts +10 -0
- package/lib/views/runViewBeforeClose.js +45 -0
- package/lib/views/useDialog.d.ts +2 -1
- package/lib/views/useDialog.js +28 -6
- package/lib/views/useDrawer.d.ts +2 -1
- package/lib/views/useDrawer.js +27 -5
- package/lib/views/usePage.d.ts +6 -1
- package/lib/views/usePage.js +53 -9
- package/lib/views/usePopover.js +4 -1
- package/lib/views/viewEvents.d.ts +17 -0
- package/lib/views/viewEvents.js +90 -0
- package/package.json +5 -5
- package/src/BlockScopedFlowEngine.ts +2 -5
- package/src/JSRunner.ts +111 -5
- package/src/ViewScopedFlowEngine.ts +8 -0
- package/src/__tests__/JSRunner.test.ts +91 -1
- package/src/__tests__/createViewMeta.popup.test.ts +62 -1
- package/src/__tests__/flowContext.test.ts +693 -1
- package/src/__tests__/flowEngine.dataSourceDirty.test.ts +63 -0
- package/src/__tests__/flowEngine.modelLoaders.test.ts +245 -0
- package/src/__tests__/flowModel.openView.navigation.test.ts +28 -0
- package/src/__tests__/flowRunJSContextDefine.test.ts +63 -0
- package/src/__tests__/flowRuntimeContext.test.ts +2 -1
- package/src/__tests__/flowSettings.open.test.tsx +123 -19
- package/src/__tests__/flowSettings.test.ts +94 -15
- package/src/__tests__/provider.test.tsx +0 -5
- package/src/__tests__/renderHiddenInConfig.test.tsx +6 -6
- package/src/__tests__/runjsContext.test.ts +23 -7
- package/src/__tests__/runjsContextImplementations.test.ts +34 -3
- package/src/__tests__/runjsContextRuntime.test.ts +3 -3
- package/src/__tests__/runjsContributions.test.ts +89 -0
- package/src/__tests__/runjsExternalLibs.test.ts +242 -0
- package/src/__tests__/runjsLibsLazyLoading.test.ts +44 -0
- package/src/__tests__/runjsLocales.test.ts +4 -1
- package/src/__tests__/runjsPreprocessDefault.test.ts +72 -0
- package/src/__tests__/runjsRuntimeFeatures.test.ts +166 -0
- package/src/__tests__/runjsSnippets.test.ts +40 -3
- package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
- package/src/acl/Acl.tsx +3 -3
- package/src/components/FlowContextSelector.tsx +208 -12
- package/src/components/MobilePopup.tsx +4 -2
- package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +3 -3
- package/src/components/__tests__/gridDragPlanner.test.ts +229 -1
- package/src/components/dnd/gridDragPlanner.ts +68 -2
- package/src/components/settings/wrappers/component/SwitchWithTitle.tsx +2 -1
- package/src/components/settings/wrappers/component/__tests__/InlineControls.test.tsx +74 -0
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +109 -16
- package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +41 -7
- package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +31 -4
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +157 -5
- package/src/components/subModel/AddSubModelButton.tsx +17 -1
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +142 -32
- package/src/components/subModel/utils.ts +1 -1
- package/src/components/variables/VariableInput.tsx +12 -4
- package/src/components/variables/VariableTag.tsx +54 -45
- package/src/components/variables/__tests__/FlowContextSelector.test.tsx +260 -3
- package/src/components/variables/__tests__/VariableTag.test.tsx +50 -0
- package/src/components/variables/__tests__/utils.test.ts +81 -3
- package/src/components/variables/utils.ts +67 -6
- package/src/data-source/index.ts +88 -110
- package/src/executor/FlowExecutor.ts +230 -28
- package/src/executor/__tests__/flowExecutor.test.ts +123 -0
- package/src/flowContext.ts +2989 -212
- package/src/flowEngine.ts +427 -22
- package/src/flowI18n.ts +7 -5
- package/src/flowSettings.ts +58 -18
- package/src/index.ts +14 -1
- package/src/lazy-helper.tsx +57 -0
- package/src/locale/en-US.json +9 -2
- package/src/locale/zh-CN.json +8 -1
- package/src/models/CollectionFieldModel.tsx +3 -1
- package/src/models/__tests__/dispatchEvent.when.test.ts +768 -0
- package/src/models/__tests__/flowModel.clone.test.ts +416 -0
- package/src/models/__tests__/flowModel.test.ts +20 -4
- package/src/models/flowModel.tsx +112 -7
- package/src/provider.tsx +9 -7
- package/src/resources/__tests__/multiRecordResource.test.ts +44 -0
- package/src/resources/__tests__/sqlResource.test.ts +60 -0
- package/src/resources/baseRecordResource.ts +31 -0
- package/src/resources/multiRecordResource.ts +11 -4
- package/src/resources/singleRecordResource.ts +3 -0
- package/src/resources/sqlResource.ts +11 -6
- package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +10 -0
- package/src/runjs-context/contexts/JSBlockRunJSContext.ts +6 -2
- package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +106 -0
- package/src/runjs-context/contexts/JSItemRunJSContext.ts +10 -0
- package/src/runjs-context/contexts/base.ts +715 -44
- package/src/runjs-context/contributions.ts +88 -0
- package/src/runjs-context/helpers.ts +11 -1
- package/src/runjs-context/registry.ts +1 -1
- package/src/runjs-context/setup.ts +24 -9
- package/src/runjs-context/snippets/global/api-request.snippet.ts +3 -3
- package/src/runjs-context/snippets/global/import-esm.snippet.ts +2 -3
- package/src/runjs-context/snippets/global/query-selector.snippet.ts +8 -3
- package/src/runjs-context/snippets/global/require-amd.snippet.ts +1 -1
- package/src/runjs-context/snippets/index.ts +75 -41
- package/src/runjs-context/snippets/scene/block/add-event-listener.snippet.ts +11 -13
- package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +6 -11
- package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +6 -6
- package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +3 -4
- package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +20 -3
- package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/form/render-basic.snippet.ts +3 -8
- package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +6 -3
- package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +3 -1
- package/src/runjsLibs.ts +622 -0
- package/src/scheduler/ModelOperationScheduler.ts +41 -24
- package/src/types.ts +86 -1
- package/src/utils/__tests__/dateVariable.test.ts +101 -0
- package/src/utils/__tests__/params-resolvers.test.ts +40 -0
- package/src/utils/__tests__/parsePathnameToViewParams.test.ts +7 -0
- package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +38 -0
- package/src/utils/__tests__/runjsTemplateCompat.test.ts +159 -0
- package/src/utils/__tests__/runjsValue.test.ts +44 -0
- package/src/utils/__tests__/safeGlobals.test.ts +57 -2
- package/src/utils/__tests__/utils.test.ts +157 -0
- package/src/utils/associationObjectVariable.ts +2 -2
- package/src/utils/createCollectionContextMeta.ts +1 -0
- package/src/utils/createEphemeralContext.ts +5 -4
- package/src/utils/dateVariable.ts +397 -0
- package/src/utils/exceptions.ts +11 -0
- package/src/utils/index.ts +38 -3
- package/src/utils/params-resolvers.ts +23 -9
- package/src/utils/parsePathnameToViewParams.ts +2 -2
- package/src/utils/resolveModuleUrl.ts +91 -0
- package/src/utils/resolveRunJSObjectValues.ts +46 -0
- package/src/utils/runjsModuleLoader.ts +553 -0
- package/src/utils/runjsTemplateCompat.ts +828 -0
- package/src/utils/runjsValue.ts +287 -0
- package/src/utils/safeGlobals.ts +188 -17
- package/src/utils/schema-utils.ts +109 -1
- package/src/views/FlowView.tsx +11 -1
- package/src/views/__tests__/FlowView.usePage.test.tsx +54 -1
- package/src/views/__tests__/runViewBeforeClose.test.ts +30 -0
- package/src/views/__tests__/useDialog.closeDestroy.test.tsx +44 -16
- package/src/views/__tests__/viewEvents.resolveOpenerEngine.test.ts +28 -0
- package/src/views/createViewMeta.ts +22 -75
- package/src/views/index.tsx +1 -2
- package/src/views/runViewBeforeClose.ts +19 -0
- package/src/views/useDialog.tsx +34 -5
- package/src/views/useDrawer.tsx +33 -4
- package/src/views/usePage.tsx +63 -8
- package/src/views/usePopover.tsx +4 -1
- package/src/views/viewEvents.ts +55 -0
package/lib/views/usePage.js
CHANGED
|
@@ -37,6 +37,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
37
37
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
38
38
|
var usePage_exports = {};
|
|
39
39
|
__export(usePage_exports, {
|
|
40
|
+
EMBED_REPLACING_DATA_KEY: () => EMBED_REPLACING_DATA_KEY,
|
|
41
|
+
GLOBAL_EMBED_CONTAINER_ID: () => GLOBAL_EMBED_CONTAINER_ID,
|
|
40
42
|
usePage: () => usePage
|
|
41
43
|
});
|
|
42
44
|
module.exports = __toCommonJS(usePage_exports);
|
|
@@ -48,22 +50,27 @@ var import_FlowContextProvider = require("../FlowContextProvider");
|
|
|
48
50
|
var import_createViewMeta = require("./createViewMeta");
|
|
49
51
|
var import_PageComponent = require("./PageComponent");
|
|
50
52
|
var import_usePatchElement = __toESM(require("./usePatchElement"));
|
|
53
|
+
var import_viewEvents = require("./viewEvents");
|
|
51
54
|
var import_provider = require("../provider");
|
|
52
55
|
var import_ViewScopedFlowEngine = require("../ViewScopedFlowEngine");
|
|
53
56
|
var import_variablesParams = require("../utils/variablesParams");
|
|
57
|
+
var import_runViewBeforeClose = require("./runViewBeforeClose");
|
|
54
58
|
let uuid = 0;
|
|
59
|
+
const GLOBAL_EMBED_CONTAINER_ID = "nocobase-embed-container";
|
|
60
|
+
const EMBED_REPLACING_DATA_KEY = "nocobaseEmbedReplacing";
|
|
55
61
|
const PageElementsHolder = import_react.default.memo(
|
|
56
62
|
import_react.default.forwardRef((props, ref) => {
|
|
57
63
|
const [elements, patchElement] = (0, import_usePatchElement.default)();
|
|
58
64
|
import_react.default.useImperativeHandle(ref, () => ({ patchElement }), [patchElement]);
|
|
59
|
-
console.log("[NocoBase] Rendering PageElementsHolder with elements count:", elements.length);
|
|
60
65
|
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, elements);
|
|
61
66
|
})
|
|
62
67
|
);
|
|
63
68
|
function usePage() {
|
|
64
69
|
const holderRef = import_react.default.useRef(null);
|
|
70
|
+
const globalEmbedActiveRef = import_react.default.useRef(null);
|
|
65
71
|
const open = /* @__PURE__ */ __name((config, flowContext) => {
|
|
66
72
|
var _a, _b, _c;
|
|
73
|
+
const parentEngine = flowContext == null ? void 0 : flowContext.engine;
|
|
67
74
|
uuid += 1;
|
|
68
75
|
const pageRef = import_react.default.createRef();
|
|
69
76
|
let closeFunc;
|
|
@@ -93,9 +100,27 @@ function usePage() {
|
|
|
93
100
|
}, [props]);
|
|
94
101
|
return null;
|
|
95
102
|
}, "HeaderComponent");
|
|
96
|
-
const {
|
|
103
|
+
const {
|
|
104
|
+
target,
|
|
105
|
+
content,
|
|
106
|
+
preventClose,
|
|
107
|
+
inheritContext = true,
|
|
108
|
+
inputArgs: viewInputArgs = {},
|
|
109
|
+
...restConfig
|
|
110
|
+
} = config;
|
|
111
|
+
const isGlobalEmbedContainer = target instanceof HTMLElement && target.id === GLOBAL_EMBED_CONTAINER_ID;
|
|
112
|
+
if (isGlobalEmbedContainer && globalEmbedActiveRef.current) {
|
|
113
|
+
try {
|
|
114
|
+
target.dataset[EMBED_REPLACING_DATA_KEY] = "1";
|
|
115
|
+
globalEmbedActiveRef.current.destroy();
|
|
116
|
+
} finally {
|
|
117
|
+
delete target.dataset[EMBED_REPLACING_DATA_KEY];
|
|
118
|
+
globalEmbedActiveRef.current = null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
97
121
|
const ctx = new import_flowContext.FlowContext();
|
|
98
122
|
const scopedEngine = (0, import_ViewScopedFlowEngine.createViewScopedEngine)(flowContext.engine);
|
|
123
|
+
const openerEngine = (0, import_viewEvents.resolveOpenerEngine)(parentEngine, scopedEngine);
|
|
99
124
|
ctx.defineProperty("engine", { value: scopedEngine });
|
|
100
125
|
ctx.addDelegate(scopedEngine.context);
|
|
101
126
|
if (inheritContext) {
|
|
@@ -105,30 +130,45 @@ function usePage() {
|
|
|
105
130
|
}
|
|
106
131
|
const currentPage = {
|
|
107
132
|
type: "embed",
|
|
108
|
-
inputArgs:
|
|
133
|
+
inputArgs: viewInputArgs,
|
|
109
134
|
preventClose: !!config.preventClose,
|
|
135
|
+
beforeClose: void 0,
|
|
110
136
|
destroy: /* @__PURE__ */ __name((result) => {
|
|
111
|
-
var _a2, _b2;
|
|
137
|
+
var _a2, _b2, _c2, _d, _e;
|
|
112
138
|
(_a2 = config.onClose) == null ? void 0 : _a2.call(config);
|
|
113
139
|
resolvePromise == null ? void 0 : resolvePromise(result);
|
|
114
140
|
(_b2 = pageRef.current) == null ? void 0 : _b2.destroy();
|
|
115
141
|
closeFunc == null ? void 0 : closeFunc();
|
|
142
|
+
if (isGlobalEmbedContainer) {
|
|
143
|
+
globalEmbedActiveRef.current = null;
|
|
144
|
+
}
|
|
145
|
+
const isReplacing = isGlobalEmbedContainer && target instanceof HTMLElement && ((_c2 = target.dataset) == null ? void 0 : _c2[EMBED_REPLACING_DATA_KEY]) === "1";
|
|
146
|
+
if (!isReplacing) {
|
|
147
|
+
const openerEmitter = openerEngine == null ? void 0 : openerEngine.emitter;
|
|
148
|
+
(0, import_viewEvents.bumpViewActivatedVersion)(openerEmitter);
|
|
149
|
+
(_e = openerEmitter == null ? void 0 : openerEmitter.emit) == null ? void 0 : _e.call(openerEmitter, import_viewEvents.VIEW_ACTIVATED_EVENT, { type: "embed", viewUid: (_d = currentPage == null ? void 0 : currentPage.inputArgs) == null ? void 0 : _d.viewUid });
|
|
150
|
+
}
|
|
116
151
|
scopedEngine.unlinkFromStack();
|
|
117
152
|
}, "destroy"),
|
|
118
153
|
update: /* @__PURE__ */ __name((newConfig) => {
|
|
119
154
|
var _a2;
|
|
120
155
|
return (_a2 = pageRef.current) == null ? void 0 : _a2.update(newConfig);
|
|
121
156
|
}, "update"),
|
|
122
|
-
close: /* @__PURE__ */ __name((result, force) => {
|
|
157
|
+
close: /* @__PURE__ */ __name(async (result, force) => {
|
|
123
158
|
var _a2, _b2;
|
|
124
159
|
if (preventClose && !force) {
|
|
125
|
-
return;
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
const shouldClose = await (0, import_runViewBeforeClose.runViewBeforeClose)(currentPage, { result, force });
|
|
163
|
+
if (!shouldClose) {
|
|
164
|
+
return false;
|
|
126
165
|
}
|
|
127
166
|
if (config.triggerByRouter && ((_b2 = (_a2 = config.inputArgs) == null ? void 0 : _a2.navigation) == null ? void 0 : _b2.back)) {
|
|
128
167
|
config.inputArgs.navigation.back();
|
|
129
|
-
return;
|
|
168
|
+
return true;
|
|
130
169
|
}
|
|
131
170
|
currentPage.destroy(result);
|
|
171
|
+
return true;
|
|
132
172
|
}, "close"),
|
|
133
173
|
Header: HeaderComponent,
|
|
134
174
|
Footer: FooterComponent,
|
|
@@ -147,7 +187,6 @@ function usePage() {
|
|
|
147
187
|
};
|
|
148
188
|
ctx.defineProperty("view", {
|
|
149
189
|
get: /* @__PURE__ */ __name(() => currentPage, "get"),
|
|
150
|
-
// meta: createViewMeta(ctx),
|
|
151
190
|
// 仅当访问关联字段或前端无本地记录数据时,才交给服务端解析
|
|
152
191
|
resolveOnServer: (0, import_variablesParams.createViewRecordResolveOnServer)(ctx, () => (0, import_variablesParams.getViewRecordFromParent)(flowContext, ctx))
|
|
153
192
|
});
|
|
@@ -186,13 +225,16 @@ function usePage() {
|
|
|
186
225
|
displayName: "PageWithContext"
|
|
187
226
|
}
|
|
188
227
|
);
|
|
189
|
-
const key = (
|
|
228
|
+
const key = (viewInputArgs == null ? void 0 : viewInputArgs.viewUid) || `page-${uuid}`;
|
|
190
229
|
const page = /* @__PURE__ */ import_react.default.createElement(import_provider.FlowEngineProvider, { key, engine: scopedEngine }, /* @__PURE__ */ import_react.default.createElement(import_FlowContextProvider.FlowViewContextProvider, { context: ctx }, /* @__PURE__ */ import_react.default.createElement(PageWithContext, null)));
|
|
191
230
|
if (target && target instanceof HTMLElement) {
|
|
192
231
|
closeFunc = (_b = holderRef.current) == null ? void 0 : _b.patchElement(import_react_dom.default.createPortal(page, target, key));
|
|
193
232
|
} else {
|
|
194
233
|
closeFunc = (_c = holderRef.current) == null ? void 0 : _c.patchElement(page);
|
|
195
234
|
}
|
|
235
|
+
if (isGlobalEmbedContainer) {
|
|
236
|
+
globalEmbedActiveRef.current = { destroy: currentPage.destroy };
|
|
237
|
+
}
|
|
196
238
|
return Object.assign(promise, currentPage);
|
|
197
239
|
}, "open");
|
|
198
240
|
const api = import_react.default.useMemo(() => ({ open }), []);
|
|
@@ -201,5 +243,7 @@ function usePage() {
|
|
|
201
243
|
__name(usePage, "usePage");
|
|
202
244
|
// Annotate the CommonJS export names for ESM import in node:
|
|
203
245
|
0 && (module.exports = {
|
|
246
|
+
EMBED_REPLACING_DATA_KEY,
|
|
247
|
+
GLOBAL_EMBED_CONTAINER_ID,
|
|
204
248
|
usePage
|
|
205
249
|
});
|
package/lib/views/usePopover.js
CHANGED
|
@@ -68,8 +68,11 @@ const PopoverComponent = React.forwardRef(({ afterClose, content, placement, rec
|
|
|
68
68
|
destroyTooltipOnHide: true,
|
|
69
69
|
content: config.content,
|
|
70
70
|
placement: config.placement,
|
|
71
|
-
getPopupContainer: () => document.body,
|
|
71
|
+
getPopupContainer: () => document.querySelector("#nocobase-app-container") || document.body,
|
|
72
72
|
onOpenChange: (nextOpen) => {
|
|
73
|
+
if (!nextOpen && config.preventClose) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
73
76
|
setVisible(nextOpen);
|
|
74
77
|
if (!nextOpen) {
|
|
75
78
|
afterClose == null ? void 0 : afterClose();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { FlowEngine } from '../flowEngine';
|
|
10
|
+
export declare const VIEW_ACTIVATED_VERSION: unique symbol;
|
|
11
|
+
export declare const VIEW_ACTIVATED_EVENT: "view:activated";
|
|
12
|
+
export declare const DATA_SOURCE_DIRTY_EVENT: "dataSource:dirty";
|
|
13
|
+
export declare const ENGINE_SCOPE_KEY: "__NOCOBASE_ENGINE_SCOPE__";
|
|
14
|
+
export declare const VIEW_ENGINE_SCOPE: "view";
|
|
15
|
+
export declare function getEmitterViewActivatedVersion(emitter: any): number;
|
|
16
|
+
export declare function bumpViewActivatedVersion(emitter: any): number;
|
|
17
|
+
export declare function resolveOpenerEngine(parentEngine: FlowEngine, scopedEngine: FlowEngine): FlowEngine | undefined;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
15
|
+
var __export = (target, all) => {
|
|
16
|
+
for (var name in all)
|
|
17
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
18
|
+
};
|
|
19
|
+
var __copyProps = (to, from, except, desc) => {
|
|
20
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
21
|
+
for (let key of __getOwnPropNames(from))
|
|
22
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
23
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
24
|
+
}
|
|
25
|
+
return to;
|
|
26
|
+
};
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var viewEvents_exports = {};
|
|
29
|
+
__export(viewEvents_exports, {
|
|
30
|
+
DATA_SOURCE_DIRTY_EVENT: () => DATA_SOURCE_DIRTY_EVENT,
|
|
31
|
+
ENGINE_SCOPE_KEY: () => ENGINE_SCOPE_KEY,
|
|
32
|
+
VIEW_ACTIVATED_EVENT: () => VIEW_ACTIVATED_EVENT,
|
|
33
|
+
VIEW_ACTIVATED_VERSION: () => VIEW_ACTIVATED_VERSION,
|
|
34
|
+
VIEW_ENGINE_SCOPE: () => VIEW_ENGINE_SCOPE,
|
|
35
|
+
bumpViewActivatedVersion: () => bumpViewActivatedVersion,
|
|
36
|
+
getEmitterViewActivatedVersion: () => getEmitterViewActivatedVersion,
|
|
37
|
+
resolveOpenerEngine: () => resolveOpenerEngine
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(viewEvents_exports);
|
|
40
|
+
const VIEW_ACTIVATED_VERSION = Symbol.for("__NOCOBASE_VIEW_ACTIVATED_VERSION__");
|
|
41
|
+
const VIEW_ACTIVATED_EVENT = "view:activated";
|
|
42
|
+
const DATA_SOURCE_DIRTY_EVENT = "dataSource:dirty";
|
|
43
|
+
const ENGINE_SCOPE_KEY = "__NOCOBASE_ENGINE_SCOPE__";
|
|
44
|
+
const VIEW_ENGINE_SCOPE = "view";
|
|
45
|
+
function getEmitterViewActivatedVersion(emitter) {
|
|
46
|
+
const raw = Reflect.get(emitter, VIEW_ACTIVATED_VERSION);
|
|
47
|
+
const num = typeof raw === "number" ? raw : Number(raw);
|
|
48
|
+
return Number.isFinite(num) && num > 0 ? num : 0;
|
|
49
|
+
}
|
|
50
|
+
__name(getEmitterViewActivatedVersion, "getEmitterViewActivatedVersion");
|
|
51
|
+
function bumpViewActivatedVersion(emitter) {
|
|
52
|
+
const current = getEmitterViewActivatedVersion(emitter);
|
|
53
|
+
if (!Object.isExtensible(emitter)) return current;
|
|
54
|
+
const next = current + 1;
|
|
55
|
+
Reflect.set(emitter, VIEW_ACTIVATED_VERSION, next);
|
|
56
|
+
return next;
|
|
57
|
+
}
|
|
58
|
+
__name(bumpViewActivatedVersion, "bumpViewActivatedVersion");
|
|
59
|
+
function isViewEngine(engine) {
|
|
60
|
+
return Reflect.get(engine, ENGINE_SCOPE_KEY) === VIEW_ENGINE_SCOPE;
|
|
61
|
+
}
|
|
62
|
+
__name(isViewEngine, "isViewEngine");
|
|
63
|
+
function findNearestViewEngine(engine) {
|
|
64
|
+
let cur = engine;
|
|
65
|
+
let guard = 0;
|
|
66
|
+
while (cur && guard++ < 50) {
|
|
67
|
+
if (isViewEngine(cur)) return cur;
|
|
68
|
+
cur = cur.previousEngine;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
__name(findNearestViewEngine, "findNearestViewEngine");
|
|
72
|
+
function resolveOpenerEngine(parentEngine, scopedEngine) {
|
|
73
|
+
if (!parentEngine) return void 0;
|
|
74
|
+
const parentViewEngine = findNearestViewEngine(parentEngine);
|
|
75
|
+
if (parentViewEngine) return parentViewEngine;
|
|
76
|
+
const previousEngine = scopedEngine == null ? void 0 : scopedEngine.previousEngine;
|
|
77
|
+
return findNearestViewEngine(previousEngine) || parentEngine;
|
|
78
|
+
}
|
|
79
|
+
__name(resolveOpenerEngine, "resolveOpenerEngine");
|
|
80
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
81
|
+
0 && (module.exports = {
|
|
82
|
+
DATA_SOURCE_DIRTY_EVENT,
|
|
83
|
+
ENGINE_SCOPE_KEY,
|
|
84
|
+
VIEW_ACTIVATED_EVENT,
|
|
85
|
+
VIEW_ACTIVATED_VERSION,
|
|
86
|
+
VIEW_ENGINE_SCOPE,
|
|
87
|
+
bumpViewActivatedVersion,
|
|
88
|
+
getEmitterViewActivatedVersion,
|
|
89
|
+
resolveOpenerEngine
|
|
90
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/flow-engine",
|
|
3
|
-
"version": "2.1.0-alpha.
|
|
3
|
+
"version": "2.1.0-alpha.10",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A standalone flow engine for NocoBase, managing workflows, models, and actions.",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@formily/antd-v5": "1.x",
|
|
10
10
|
"@formily/reactive": "2.x",
|
|
11
|
-
"@nocobase/sdk": "2.1.0-alpha.
|
|
12
|
-
"@nocobase/shared": "2.1.0-alpha.
|
|
11
|
+
"@nocobase/sdk": "2.1.0-alpha.10",
|
|
12
|
+
"@nocobase/shared": "2.1.0-alpha.10",
|
|
13
13
|
"ahooks": "^3.7.2",
|
|
14
14
|
"dayjs": "^1.11.9",
|
|
15
15
|
"dompurify": "^3.0.2",
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
"workflow"
|
|
36
36
|
],
|
|
37
37
|
"author": "NocoBase Team",
|
|
38
|
-
"license": "
|
|
39
|
-
"gitHead": "
|
|
38
|
+
"license": "Apache-2.0",
|
|
39
|
+
"gitHead": "ce790d46c0a5768ca9618c7d0d77ab8300de75c8"
|
|
40
40
|
}
|
|
@@ -31,16 +31,13 @@ export function createBlockScopedEngine(parent: FlowEngine): FlowEngine {
|
|
|
31
31
|
local.context.addDelegate(parent.context);
|
|
32
32
|
|
|
33
33
|
// 覆盖 unlinkFromStack:BlockScoped 引擎被移除时,修复前后指针,避免“截断”后续视图/作用域
|
|
34
|
-
const originalUnlink = local.unlinkFromStack.bind(local);
|
|
35
34
|
local.unlinkFromStack = function () {
|
|
36
|
-
// 修复指针:prev -> next,next -> prev,然后清理自身指针
|
|
37
|
-
// 若不这么做,移除位于中间的 block 引擎会导致后续整段链丢失
|
|
38
35
|
const prev = (local as any)._previousEngine as FlowEngine | undefined;
|
|
39
36
|
const next = (local as any)._nextEngine as FlowEngine | undefined;
|
|
40
37
|
if (prev) (prev as any)._nextEngine = next;
|
|
41
38
|
if (next) (next as any)._previousEngine = prev;
|
|
42
|
-
(local as any)._previousEngine = undefined
|
|
43
|
-
(local as any)._nextEngine = undefined
|
|
39
|
+
(local as any)._previousEngine = undefined;
|
|
40
|
+
(local as any)._nextEngine = undefined;
|
|
44
41
|
};
|
|
45
42
|
|
|
46
43
|
// 默认全部代理到父引擎,只有少数字段(实例/缓存/执行器/上下文/链表指针)使用本地值
|
package/src/JSRunner.ts
CHANGED
|
@@ -8,11 +8,80 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import 'ses';
|
|
11
|
+
import { FlowExitAllException, FlowExitException } from './utils/exceptions';
|
|
11
12
|
|
|
12
13
|
export interface JSRunnerOptions {
|
|
13
14
|
timeoutMs?: number;
|
|
14
15
|
globals?: Record<string, any>;
|
|
15
16
|
version?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Enable RunJS template compatibility preprocessing for `{{ ... }}`.
|
|
19
|
+
* When enabled (or falling back to version default),
|
|
20
|
+
* the code will be rewritten to call `ctx.resolveJsonTemplate(...)` at runtime.
|
|
21
|
+
*/
|
|
22
|
+
preprocessTemplates?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Decide whether RunJS `{{ ... }}` compatibility preprocessing should run.
|
|
27
|
+
*
|
|
28
|
+
* Priority:
|
|
29
|
+
* 1. Explicit `preprocessTemplates` option always wins.
|
|
30
|
+
* 2. Otherwise, `version === 'v2'` disables preprocessing.
|
|
31
|
+
* 3. Fallback keeps v1-compatible behavior (enabled).
|
|
32
|
+
*/
|
|
33
|
+
export function shouldPreprocessRunJSTemplates(
|
|
34
|
+
options?: Pick<JSRunnerOptions, 'preprocessTemplates' | 'version'>,
|
|
35
|
+
): boolean {
|
|
36
|
+
if (typeof options?.preprocessTemplates === 'boolean') {
|
|
37
|
+
return options.preprocessTemplates;
|
|
38
|
+
}
|
|
39
|
+
return options?.version !== 'v2';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Heuristic: detect likely bare `{{ctx.xxx}}` usage in executable positions (not quoted string literals).
|
|
43
|
+
const BARE_CTX_TEMPLATE_RE = /(^|[=(:,[\s)])(\{\{\s*(ctx(?:\.|\[|\?\.)[^}]*)\s*\}\})/m;
|
|
44
|
+
|
|
45
|
+
function extractDeprecatedCtxTemplateUsage(code: string): { placeholder: string; expression: string } | null {
|
|
46
|
+
const src = String(code || '');
|
|
47
|
+
const m = src.match(BARE_CTX_TEMPLATE_RE);
|
|
48
|
+
if (!m) return null;
|
|
49
|
+
const placeholder = String(m[2] || '').trim();
|
|
50
|
+
const expression = String(m[3] || '').trim();
|
|
51
|
+
if (!placeholder || !expression) return null;
|
|
52
|
+
return { placeholder, expression };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function shouldHintCtxTemplateSyntax(err: any, usage: { placeholder: string; expression: string } | null): boolean {
|
|
56
|
+
const isSyntaxError = err instanceof SyntaxError || String((err as any)?.name || '') === 'SyntaxError';
|
|
57
|
+
if (!isSyntaxError) return false;
|
|
58
|
+
if (!usage) return false;
|
|
59
|
+
const msg = String((err as any)?.message || err || '');
|
|
60
|
+
return /unexpected token/i.test(msg);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function toCtxTemplateSyntaxHintError(
|
|
64
|
+
err: any,
|
|
65
|
+
usage: {
|
|
66
|
+
placeholder: string;
|
|
67
|
+
expression: string;
|
|
68
|
+
},
|
|
69
|
+
): Error {
|
|
70
|
+
const hint = `"${usage.placeholder}" has been deprecated and cannot be used as executable RunJS syntax. Use await ctx.getVar("${usage.expression}") instead, or keep "${usage.placeholder}" as a plain string.`;
|
|
71
|
+
const out = new SyntaxError(hint);
|
|
72
|
+
try {
|
|
73
|
+
(out as any).cause = err;
|
|
74
|
+
} catch (_) {
|
|
75
|
+
// ignore
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
// Hint-only error: avoid leaking internal bundle line numbers from stack parsers in preview UI.
|
|
79
|
+
(out as any).__runjsHideLocation = true;
|
|
80
|
+
out.stack = `${out.name}: ${out.message}`;
|
|
81
|
+
} catch (_) {
|
|
82
|
+
// ignore
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
16
85
|
}
|
|
17
86
|
|
|
18
87
|
export class JSRunner {
|
|
@@ -28,13 +97,41 @@ export class JSRunner {
|
|
|
28
97
|
return typeof fn === 'function' ? fn.bind(globalThis) : fn;
|
|
29
98
|
};
|
|
30
99
|
|
|
100
|
+
const providedGlobals = options.globals || {};
|
|
101
|
+
const liftedGlobals: Record<string, any> = {};
|
|
102
|
+
|
|
103
|
+
// Auto-lift selected globals from safe window into top-level sandbox globals
|
|
104
|
+
// so user code can access them directly (e.g. `new Blob(...)`).
|
|
105
|
+
if (!Object.prototype.hasOwnProperty.call(providedGlobals, 'Blob')) {
|
|
106
|
+
try {
|
|
107
|
+
const blobCtor = (providedGlobals as any).window?.Blob;
|
|
108
|
+
if (typeof blobCtor !== 'undefined') {
|
|
109
|
+
liftedGlobals.Blob = blobCtor;
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// ignore when window proxy blocks property access
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!Object.prototype.hasOwnProperty.call(providedGlobals, 'URL')) {
|
|
117
|
+
try {
|
|
118
|
+
const urlCtor = (providedGlobals as any).window?.URL;
|
|
119
|
+
if (typeof urlCtor !== 'undefined') {
|
|
120
|
+
liftedGlobals.URL = urlCtor;
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
// ignore when window proxy blocks property access
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
31
127
|
this.globals = {
|
|
32
128
|
console,
|
|
33
129
|
setTimeout: bindWindowFn('setTimeout'),
|
|
34
130
|
clearTimeout: bindWindowFn('clearTimeout'),
|
|
35
131
|
setInterval: bindWindowFn('setInterval'),
|
|
36
132
|
clearInterval: bindWindowFn('clearInterval'),
|
|
37
|
-
...
|
|
133
|
+
...liftedGlobals,
|
|
134
|
+
...providedGlobals,
|
|
38
135
|
};
|
|
39
136
|
this.timeoutMs = options.timeoutMs ?? 5000; // 默认 5 秒超时
|
|
40
137
|
}
|
|
@@ -55,7 +152,8 @@ export class JSRunner {
|
|
|
55
152
|
error?: any;
|
|
56
153
|
timeout?: boolean;
|
|
57
154
|
}> {
|
|
58
|
-
|
|
155
|
+
const search = typeof location !== 'undefined' ? location.search : undefined;
|
|
156
|
+
if (typeof search === 'string' && search.includes('skipRunJs=true')) {
|
|
59
157
|
return { success: true, value: null };
|
|
60
158
|
}
|
|
61
159
|
const wrapped = `(async () => {
|
|
@@ -76,11 +174,19 @@ export class JSRunner {
|
|
|
76
174
|
const result = await Promise.race([task, timeoutPromise]);
|
|
77
175
|
return { success: true, value: result };
|
|
78
176
|
} catch (err) {
|
|
79
|
-
|
|
177
|
+
if (err instanceof FlowExitException) {
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
180
|
+
if (err instanceof FlowExitAllException) {
|
|
181
|
+
throw err;
|
|
182
|
+
}
|
|
183
|
+
const usage = extractDeprecatedCtxTemplateUsage(code);
|
|
184
|
+
const outErr = shouldHintCtxTemplateSyntax(err, usage) && usage ? toCtxTemplateSyntaxHintError(err, usage) : err;
|
|
185
|
+
console.error(outErr);
|
|
80
186
|
return {
|
|
81
187
|
success: false,
|
|
82
|
-
error:
|
|
83
|
-
timeout:
|
|
188
|
+
error: outErr,
|
|
189
|
+
timeout: (outErr as any)?.message === 'Execution timed out',
|
|
84
190
|
};
|
|
85
191
|
}
|
|
86
192
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { FlowEngine } from './flowEngine';
|
|
11
|
+
import { ENGINE_SCOPE_KEY, VIEW_ENGINE_SCOPE } from './views/viewEvents';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* ViewScopedFlowEngine(视图作用域引擎)
|
|
@@ -24,6 +25,8 @@ import { FlowEngine } from './flowEngine';
|
|
|
24
25
|
*/
|
|
25
26
|
export function createViewScopedEngine(parent: FlowEngine): FlowEngine {
|
|
26
27
|
const local = new FlowEngine();
|
|
28
|
+
// Mark for view-stack traversal (used by view activation events).
|
|
29
|
+
Object.defineProperty(local, ENGINE_SCOPE_KEY, { value: VIEW_ENGINE_SCOPE, configurable: true });
|
|
27
30
|
if (parent.modelRepository) {
|
|
28
31
|
local.setModelRepository(parent.modelRepository);
|
|
29
32
|
}
|
|
@@ -43,6 +46,7 @@ export function createViewScopedEngine(parent: FlowEngine): FlowEngine {
|
|
|
43
46
|
'_applyFlowCache',
|
|
44
47
|
'executor',
|
|
45
48
|
'context',
|
|
49
|
+
ENGINE_SCOPE_KEY,
|
|
46
50
|
'previousEngine',
|
|
47
51
|
'nextEngine',
|
|
48
52
|
// 调度器与事件总线局部化
|
|
@@ -58,6 +62,10 @@ export function createViewScopedEngine(parent: FlowEngine): FlowEngine {
|
|
|
58
62
|
'_nextEngine',
|
|
59
63
|
// getModel 需要在本地执行以确保全局查找时正确遍历整个引擎栈
|
|
60
64
|
'getModel',
|
|
65
|
+
// 视图销毁回调需要在本地存储,每个视图引擎有自己的销毁逻辑
|
|
66
|
+
'_destroyView',
|
|
67
|
+
'setDestroyView',
|
|
68
|
+
'destroyView',
|
|
61
69
|
]);
|
|
62
70
|
|
|
63
71
|
const handler: ProxyHandler<FlowEngine> = {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
11
|
-
import { JSRunner } from '../JSRunner';
|
|
11
|
+
import { JSRunner, shouldPreprocessRunJSTemplates } from '../JSRunner';
|
|
12
|
+
import { createSafeWindow } from '../utils';
|
|
12
13
|
|
|
13
14
|
describe('JSRunner', () => {
|
|
14
15
|
let originalSearch: string;
|
|
@@ -29,6 +30,18 @@ describe('JSRunner', () => {
|
|
|
29
30
|
vi.restoreAllMocks();
|
|
30
31
|
});
|
|
31
32
|
|
|
33
|
+
it('shouldPreprocessRunJSTemplates: explicit option has highest priority', () => {
|
|
34
|
+
expect(shouldPreprocessRunJSTemplates({ version: 'v2', preprocessTemplates: true })).toBe(true);
|
|
35
|
+
expect(shouldPreprocessRunJSTemplates({ version: 'v1', preprocessTemplates: false })).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('shouldPreprocessRunJSTemplates: falls back to version policy', () => {
|
|
39
|
+
expect(shouldPreprocessRunJSTemplates({ version: 'v1' })).toBe(true);
|
|
40
|
+
expect(shouldPreprocessRunJSTemplates({ version: 'v2' })).toBe(false);
|
|
41
|
+
expect(shouldPreprocessRunJSTemplates({})).toBe(true);
|
|
42
|
+
expect(shouldPreprocessRunJSTemplates()).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
32
45
|
it('executes simple code and returns value', async () => {
|
|
33
46
|
const runner = new JSRunner();
|
|
34
47
|
const result = await runner.run('return 1 + 2 + 3');
|
|
@@ -48,6 +61,69 @@ describe('JSRunner', () => {
|
|
|
48
61
|
expect(res2.value).toBe('baz');
|
|
49
62
|
});
|
|
50
63
|
|
|
64
|
+
it('auto-lifts Blob from injected window to top-level globals', async () => {
|
|
65
|
+
if (typeof Blob === 'undefined') {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const runner = new JSRunner({
|
|
70
|
+
globals: {
|
|
71
|
+
window: createSafeWindow(),
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const result = await runner.run('return new Blob(["x"]).size');
|
|
76
|
+
expect(result.success).toBe(true);
|
|
77
|
+
expect(result.value).toBe(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('keeps explicit globals.Blob higher priority than auto-lifted Blob', async () => {
|
|
81
|
+
const explicitBlob = function ExplicitBlob(this: any, chunks: any[]) {
|
|
82
|
+
this.size = Array.isArray(chunks) ? chunks.length : 0;
|
|
83
|
+
} as any;
|
|
84
|
+
|
|
85
|
+
const runner = new JSRunner({
|
|
86
|
+
globals: {
|
|
87
|
+
window: createSafeWindow(),
|
|
88
|
+
Blob: explicitBlob,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const result = await runner.run('const b = new Blob([1,2,3]); return b.size;');
|
|
93
|
+
expect(result.success).toBe(true);
|
|
94
|
+
expect(result.value).toBe(3);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('auto-lifts URL from injected window to top-level globals', async () => {
|
|
98
|
+
const runner = new JSRunner({
|
|
99
|
+
globals: {
|
|
100
|
+
window: createSafeWindow(),
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const result = await runner.run('return typeof URL.createObjectURL === "function"');
|
|
105
|
+
expect(result.success).toBe(true);
|
|
106
|
+
expect(result.value).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('keeps explicit globals.URL higher priority than auto-lifted URL', async () => {
|
|
110
|
+
const explicitURL = {
|
|
111
|
+
createObjectURL: () => 'explicit://url',
|
|
112
|
+
revokeObjectURL: (_url: string) => undefined,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const runner = new JSRunner({
|
|
116
|
+
globals: {
|
|
117
|
+
window: createSafeWindow(),
|
|
118
|
+
URL: explicitURL,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const result = await runner.run('return URL.createObjectURL(new Blob(["x"]))');
|
|
123
|
+
expect(result.success).toBe(true);
|
|
124
|
+
expect(result.value).toBe('explicit://url');
|
|
125
|
+
});
|
|
126
|
+
|
|
51
127
|
it('exposes console in sandbox by default', async () => {
|
|
52
128
|
const runner = new JSRunner();
|
|
53
129
|
const result = await runner.run('return typeof console !== "undefined"');
|
|
@@ -88,6 +164,20 @@ describe('JSRunner', () => {
|
|
|
88
164
|
expect((result.error as Error).message).toBe('Execution timed out');
|
|
89
165
|
});
|
|
90
166
|
|
|
167
|
+
it('returns friendly hint when bare {{ctx.xxx}} appears in syntax error', async () => {
|
|
168
|
+
const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
169
|
+
const runner = new JSRunner();
|
|
170
|
+
const result = await runner.run('const z = {{ctx.user.id}}');
|
|
171
|
+
expect(result.success).toBe(false);
|
|
172
|
+
expect(result.error).toBeInstanceOf(SyntaxError);
|
|
173
|
+
const msg = String((result.error as any)?.message || '');
|
|
174
|
+
expect(msg).toContain('"{{ctx.user.id}}" has been deprecated');
|
|
175
|
+
expect(msg).toContain('await ctx.getVar("ctx.user.id")');
|
|
176
|
+
expect(msg).not.toContain('(at ');
|
|
177
|
+
expect((result.error as any)?.__runjsHideLocation).toBe(true);
|
|
178
|
+
expect(spy).toHaveBeenCalled();
|
|
179
|
+
});
|
|
180
|
+
|
|
91
181
|
it('skips execution when URL contains skipRunJs=true', async () => {
|
|
92
182
|
// 模拟预览模式下通过 URL 参数跳过代码执行
|
|
93
183
|
if (typeof window !== 'undefined' && typeof window.history?.pushState === 'function') {
|