@nocobase/flow-engine 2.1.0-beta.42 → 2.1.0-beta.44
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/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +76 -31
- package/lib/components/subModel/LazyDropdown.js +17 -9
- package/lib/executor/FlowExecutor.js +0 -3
- package/lib/flowContext.d.ts +6 -1
- package/lib/flowContext.js +35 -6
- package/lib/flowEngine.d.ts +4 -3
- package/lib/flowEngine.js +69 -37
- package/lib/models/flowModel.js +45 -13
- package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +4 -3
- package/lib/runjs-context/contexts/JSBlockRunJSContext.js +4 -15
- package/lib/runjs-context/contexts/JSColumnRunJSContext.js +5 -2
- package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +5 -8
- package/lib/runjs-context/contexts/JSFieldRunJSContext.js +4 -3
- package/lib/runjs-context/contexts/JSItemRunJSContext.js +4 -3
- package/lib/runjs-context/contexts/base.js +464 -29
- package/lib/runjs-context/contexts/elementDoc.d.ts +11 -0
- package/lib/runjs-context/contexts/elementDoc.js +152 -0
- package/lib/utils/loadedPageCache.d.ts +24 -0
- package/lib/utils/loadedPageCache.js +139 -0
- package/package.json +4 -4
- package/src/__tests__/flowContext.test.ts +23 -0
- package/src/__tests__/flowEngine.moveModel.test.ts +81 -1
- package/src/__tests__/runjsContext.test.ts +18 -0
- package/src/__tests__/runjsContextImplementations.test.ts +9 -2
- package/src/__tests__/runjsLocales.test.ts +6 -5
- package/src/__tests__/viewScopedFlowEngine.test.ts +133 -0
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +79 -37
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +150 -3
- package/src/components/subModel/LazyDropdown.tsx +16 -7
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +51 -0
- package/src/executor/FlowExecutor.ts +0 -3
- package/src/executor/__tests__/flowExecutor.test.ts +2 -4
- package/src/flowContext.ts +40 -6
- package/src/flowEngine.ts +71 -35
- package/src/models/__tests__/flowModel.test.ts +13 -28
- package/src/models/flowModel.tsx +62 -29
- package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +4 -3
- package/src/runjs-context/contexts/JSBlockRunJSContext.ts +4 -15
- package/src/runjs-context/contexts/JSColumnRunJSContext.ts +4 -2
- package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +5 -9
- package/src/runjs-context/contexts/JSFieldRunJSContext.ts +4 -3
- package/src/runjs-context/contexts/JSItemRunJSContext.ts +4 -3
- package/src/runjs-context/contexts/base.ts +467 -31
- package/src/runjs-context/contexts/elementDoc.ts +130 -0
- package/src/utils/loadedPageCache.ts +147 -0
|
@@ -207,8 +207,17 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
207
207
|
const [visible, setVisible] = (0, import_react.useState)(false);
|
|
208
208
|
const [refreshTick, setRefreshTick] = (0, import_react.useState)(0);
|
|
209
209
|
const [extraMenuItems, setExtraMenuItems] = (0, import_react.useState)([]);
|
|
210
|
+
const [extraMenuItemsLoaded, setExtraMenuItemsLoaded] = (0, import_react.useState)(false);
|
|
210
211
|
const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = (0, import_react.useState)([]);
|
|
211
212
|
const [isLoading, setIsLoading] = (0, import_react.useState)(true);
|
|
213
|
+
const commonExtras = (0, import_react.useMemo)(
|
|
214
|
+
() => extraMenuItems.filter((it) => it.group === "common-actions").sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)),
|
|
215
|
+
[extraMenuItems]
|
|
216
|
+
);
|
|
217
|
+
const hasCommonActions = showCopyUidButton || showDeleteButton || commonExtras.length > 0;
|
|
218
|
+
const shouldDeferConfigLoading = flattenSubMenus && menuLevels > 1 && hasCommonActions;
|
|
219
|
+
const shouldWaitForCommonActionProbe = flattenSubMenus && menuLevels > 1 && !showCopyUidButton && !showDeleteButton && !extraMenuItemsLoaded;
|
|
220
|
+
const canRenderIcon = hasCommonActions || !isLoading && configurableFlowsAndSteps.length > 0;
|
|
212
221
|
const closeDropdown = (0, import_react.useCallback)(() => {
|
|
213
222
|
setVisible(false);
|
|
214
223
|
onDropdownVisibleChange == null ? void 0 : onDropdownVisibleChange(false);
|
|
@@ -240,24 +249,28 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
240
249
|
let mounted = true;
|
|
241
250
|
const loadExtras = /* @__PURE__ */ __name(async () => {
|
|
242
251
|
var _a;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
modelsToProcess
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
252
|
+
setExtraMenuItemsLoaded(false);
|
|
253
|
+
try {
|
|
254
|
+
const allExtras = [];
|
|
255
|
+
const modelsToProcess = [];
|
|
256
|
+
walkSubModels(model, { maxDepth: menuLevels, arrayLimit: 50, mode: "stack" }, (targetModel, { modelKey }) => {
|
|
257
|
+
modelsToProcess.push({ model: targetModel, modelKey });
|
|
258
|
+
});
|
|
259
|
+
for (const { model: targetModel, modelKey } of modelsToProcess) {
|
|
260
|
+
const Cls = targetModel.constructor;
|
|
261
|
+
const extras = await ((_a = Cls.getExtraMenuItems) == null ? void 0 : _a.call(Cls, targetModel, t));
|
|
262
|
+
if (extras == null ? void 0 : extras.length) {
|
|
263
|
+
allExtras.push(
|
|
264
|
+
...extras.map((item) => ({
|
|
265
|
+
...item,
|
|
266
|
+
key: modelKey ? `${modelKey}:${item.key}` : item.key
|
|
267
|
+
}))
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (!mounted) {
|
|
272
|
+
return;
|
|
258
273
|
}
|
|
259
|
-
}
|
|
260
|
-
if (mounted) {
|
|
261
274
|
const seen = /* @__PURE__ */ new Set();
|
|
262
275
|
const dedupedExtras = allExtras.filter((item) => {
|
|
263
276
|
if (seen.has(`${item.key}`)) {
|
|
@@ -267,15 +280,22 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
267
280
|
return true;
|
|
268
281
|
});
|
|
269
282
|
setExtraMenuItems(dedupedExtras);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.error("Failed to load extra menu items:", error);
|
|
285
|
+
if (mounted) {
|
|
286
|
+
setExtraMenuItems([]);
|
|
287
|
+
}
|
|
288
|
+
} finally {
|
|
289
|
+
if (mounted) {
|
|
290
|
+
setExtraMenuItemsLoaded(true);
|
|
291
|
+
}
|
|
270
292
|
}
|
|
271
293
|
}, "loadExtras");
|
|
272
|
-
|
|
273
|
-
loadExtras();
|
|
274
|
-
}
|
|
294
|
+
loadExtras();
|
|
275
295
|
return () => {
|
|
276
296
|
mounted = false;
|
|
277
297
|
};
|
|
278
|
-
}, [model, menuLevels, t, refreshTick
|
|
298
|
+
}, [model, menuLevels, t, refreshTick]);
|
|
279
299
|
const copyUidToClipboard = (0, import_react.useCallback)(
|
|
280
300
|
async (uid) => {
|
|
281
301
|
var _a;
|
|
@@ -520,7 +540,7 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
520
540
|
return [];
|
|
521
541
|
}
|
|
522
542
|
},
|
|
523
|
-
[]
|
|
543
|
+
[t]
|
|
524
544
|
);
|
|
525
545
|
const getConfigurableFlowsAndSteps = (0, import_react.useCallback)(async () => {
|
|
526
546
|
const result = [];
|
|
@@ -558,20 +578,47 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
558
578
|
};
|
|
559
579
|
}, [model, menuLevels, refreshTick]);
|
|
560
580
|
(0, import_react.useEffect)(() => {
|
|
581
|
+
let mounted = true;
|
|
561
582
|
const loadConfigurableFlowsAndSteps = /* @__PURE__ */ __name(async () => {
|
|
562
583
|
setIsLoading(true);
|
|
584
|
+
if (shouldDeferConfigLoading) {
|
|
585
|
+
setConfigurableFlowsAndSteps([]);
|
|
586
|
+
}
|
|
563
587
|
try {
|
|
564
588
|
const flows = await getConfigurableFlowsAndSteps();
|
|
565
|
-
|
|
589
|
+
if (mounted) {
|
|
590
|
+
setConfigurableFlowsAndSteps(flows);
|
|
591
|
+
}
|
|
566
592
|
} catch (error) {
|
|
567
593
|
console.error("Failed to load configurable flows and steps:", error);
|
|
568
|
-
|
|
594
|
+
if (mounted) {
|
|
595
|
+
setConfigurableFlowsAndSteps([]);
|
|
596
|
+
}
|
|
569
597
|
} finally {
|
|
570
|
-
|
|
598
|
+
if (mounted) {
|
|
599
|
+
setIsLoading(false);
|
|
600
|
+
}
|
|
571
601
|
}
|
|
572
602
|
}, "loadConfigurableFlowsAndSteps");
|
|
603
|
+
if (shouldWaitForCommonActionProbe) {
|
|
604
|
+
setConfigurableFlowsAndSteps([]);
|
|
605
|
+
setIsLoading(false);
|
|
606
|
+
return () => {
|
|
607
|
+
mounted = false;
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
if (!visible && shouldDeferConfigLoading) {
|
|
611
|
+
setConfigurableFlowsAndSteps([]);
|
|
612
|
+
setIsLoading(false);
|
|
613
|
+
return () => {
|
|
614
|
+
mounted = false;
|
|
615
|
+
};
|
|
616
|
+
}
|
|
573
617
|
loadConfigurableFlowsAndSteps();
|
|
574
|
-
|
|
618
|
+
return () => {
|
|
619
|
+
mounted = false;
|
|
620
|
+
};
|
|
621
|
+
}, [getConfigurableFlowsAndSteps, refreshTick, shouldDeferConfigLoading, shouldWaitForCommonActionProbe, visible]);
|
|
575
622
|
const menuItems = (0, import_react.useMemo)(() => {
|
|
576
623
|
const items = [];
|
|
577
624
|
const keyCounter = /* @__PURE__ */ new Map();
|
|
@@ -696,10 +743,9 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
696
743
|
}
|
|
697
744
|
}
|
|
698
745
|
return items;
|
|
699
|
-
}, [configurableFlowsAndSteps, disabledIconColor, flattenSubMenus, t]);
|
|
746
|
+
}, [configurableFlowsAndSteps, disabledIconColor, flattenSubMenus, message, model, t]);
|
|
700
747
|
const finalMenuItems = (0, import_react.useMemo)(() => {
|
|
701
748
|
const items = [...menuItems];
|
|
702
|
-
const commonExtras = extraMenuItems.filter((it) => it.group === "common-actions").sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
|
|
703
749
|
if (showCopyUidButton || showDeleteButton || commonExtras.length > 0) {
|
|
704
750
|
items.push({
|
|
705
751
|
type: "divider"
|
|
@@ -721,9 +767,8 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
721
767
|
}
|
|
722
768
|
}
|
|
723
769
|
return items;
|
|
724
|
-
}, [menuItems, showCopyUidButton, showDeleteButton, model.uid, model.destroy, t
|
|
725
|
-
|
|
726
|
-
if (isLoading || configurableFlowsAndSteps.length === 0 && !showDeleteButton && !showCopyUidButton && !hasExtras) {
|
|
770
|
+
}, [menuItems, showCopyUidButton, showDeleteButton, commonExtras, model.uid, model.destroy, t]);
|
|
771
|
+
if (!canRenderIcon) {
|
|
727
772
|
return null;
|
|
728
773
|
}
|
|
729
774
|
if (!model || !model.uid) {
|
|
@@ -332,7 +332,7 @@ const getLabelSearchText = /* @__PURE__ */ __name((label) => {
|
|
|
332
332
|
}
|
|
333
333
|
return "";
|
|
334
334
|
}, "getLabelSearchText");
|
|
335
|
-
const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchValue, menuVisible, t, searchHandlers, activateSearchSubmenu, deactivateSearchSubmenu) => ({
|
|
335
|
+
const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchValue, menuVisible, t, searchHandlers, activateSearchSubmenu, deactivateSearchSubmenu, shouldActivateSearchSubmenu) => ({
|
|
336
336
|
key: `${searchKey}-search`,
|
|
337
337
|
type: "group",
|
|
338
338
|
label: /* @__PURE__ */ import_react.default.createElement("div", { onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ import_react.default.createElement(
|
|
@@ -350,28 +350,34 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
350
350
|
var _a;
|
|
351
351
|
e.stopPropagation();
|
|
352
352
|
const value = e.target.value;
|
|
353
|
-
|
|
353
|
+
if (shouldActivateSearchSubmenu) {
|
|
354
|
+
activateSearchSubmenu(searchKey);
|
|
355
|
+
}
|
|
354
356
|
if (((_a = e.nativeEvent) == null ? void 0 : _a.isComposing) || searchHandlers.isComposing(searchKey)) {
|
|
355
357
|
searchHandlers.updateInputValue(searchKey, value);
|
|
356
358
|
return;
|
|
357
359
|
}
|
|
358
|
-
if (!value) {
|
|
360
|
+
if (!value && shouldActivateSearchSubmenu) {
|
|
359
361
|
deactivateSearchSubmenu(searchKey);
|
|
360
362
|
}
|
|
361
363
|
searchHandlers.updateSearchValue(searchKey, value);
|
|
362
364
|
},
|
|
363
365
|
onCompositionStart: (e) => {
|
|
364
366
|
e.stopPropagation();
|
|
365
|
-
|
|
367
|
+
if (shouldActivateSearchSubmenu) {
|
|
368
|
+
activateSearchSubmenu(searchKey);
|
|
369
|
+
}
|
|
366
370
|
searchHandlers.startComposition(searchKey);
|
|
367
371
|
},
|
|
368
372
|
onCompositionEnd: (e) => {
|
|
369
373
|
e.stopPropagation();
|
|
370
374
|
const value = e.currentTarget.value;
|
|
371
|
-
if (
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
+
if (shouldActivateSearchSubmenu) {
|
|
376
|
+
if (value) {
|
|
377
|
+
activateSearchSubmenu(searchKey);
|
|
378
|
+
} else {
|
|
379
|
+
deactivateSearchSubmenu(searchKey);
|
|
380
|
+
}
|
|
375
381
|
}
|
|
376
382
|
searchHandlers.endComposition(searchKey, value);
|
|
377
383
|
},
|
|
@@ -557,6 +563,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
557
563
|
const searchKey = keyPath;
|
|
558
564
|
const currentSearchValue = searchValues[searchKey] || "";
|
|
559
565
|
const currentInputValue = inputValues[searchKey] ?? currentSearchValue;
|
|
566
|
+
const shouldActivateSearchSubmenu = !(item.type === "group" && path.length === 0);
|
|
560
567
|
const filteredChildren = currentSearchValue ? (/* @__PURE__ */ __name(function deepFilter(items2) {
|
|
561
568
|
const searchText = currentSearchValue.toLowerCase();
|
|
562
569
|
return items2.map((child) => {
|
|
@@ -581,7 +588,8 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
581
588
|
t,
|
|
582
589
|
searchHandlers,
|
|
583
590
|
activateSearchSubmenu,
|
|
584
|
-
deactivateSearchSubmenu
|
|
591
|
+
deactivateSearchSubmenu,
|
|
592
|
+
shouldActivateSearchSubmenu
|
|
585
593
|
);
|
|
586
594
|
const dividerItem = { key: `${keyPath}-search-divider`, type: "divider" };
|
|
587
595
|
if (currentSearchValue && resolvedFiltered.length === 0) {
|
|
@@ -153,9 +153,6 @@ const _FlowExecutor = class _FlowExecutor {
|
|
|
153
153
|
const stepDefaultParams = await (0, import_utils.resolveDefaultParams)(step.defaultParams, runtimeCtx);
|
|
154
154
|
combinedParams = { ...stepDefaultParams };
|
|
155
155
|
} else {
|
|
156
|
-
flowContext.logger.warn(
|
|
157
|
-
`BaseModel.applyFlow: Step '${stepKey}' in flow '${flowKey}' has neither 'use' nor 'handler'. Skipping.`
|
|
158
|
-
);
|
|
159
156
|
continue;
|
|
160
157
|
}
|
|
161
158
|
const modelStepParams = model.getStepParams(flowKey, stepKey);
|
package/lib/flowContext.d.ts
CHANGED
|
@@ -222,6 +222,10 @@ export type FlowContextGetApiInfosOptions = {
|
|
|
222
222
|
* RunJS 文档版本(默认 v1)。
|
|
223
223
|
*/
|
|
224
224
|
version?: RunJSVersion;
|
|
225
|
+
/**
|
|
226
|
+
* Include editor completion metadata. Defaults to false so API-doc callers keep the compact public shape.
|
|
227
|
+
*/
|
|
228
|
+
includeCompletion?: boolean;
|
|
225
229
|
};
|
|
226
230
|
export type FlowContextGetVarInfosOptions = {
|
|
227
231
|
/**
|
|
@@ -293,7 +297,7 @@ export declare class FlowContext {
|
|
|
293
297
|
* - 输出仅来自 RunJS doc 与 defineProperty/defineMethod 的 info
|
|
294
298
|
* - 不读取/展开 PropertyMeta(变量结构)
|
|
295
299
|
* - 不自动展开深层 properties
|
|
296
|
-
* -
|
|
300
|
+
* - 默认不返回自动补全字段(例如 completion),传入 includeCompletion=true 时返回
|
|
297
301
|
*/
|
|
298
302
|
getApiInfos(options?: FlowContextGetApiInfosOptions): Promise<Record<string, FlowContextApiInfo>>;
|
|
299
303
|
/**
|
|
@@ -423,6 +427,7 @@ export declare class FlowRuntimeContext<TModel extends FlowModel = FlowModel, TM
|
|
|
423
427
|
export type FlowSettingsContext<TModel extends FlowModel = FlowModel> = FlowRuntimeContext<TModel, 'settings'>;
|
|
424
428
|
export type RunJSDocCompletionDoc = {
|
|
425
429
|
insertText?: string;
|
|
430
|
+
requires?: Array<'element'>;
|
|
426
431
|
};
|
|
427
432
|
export type RunJSDocHiddenDoc = boolean | ((ctx: any) => boolean | Promise<boolean>);
|
|
428
433
|
export type RunJSDocHiddenOrPathsDoc = boolean | string[] | ((ctx: any) => boolean | string[] | Promise<boolean | string[]>);
|
package/lib/flowContext.js
CHANGED
|
@@ -399,10 +399,11 @@ const _FlowContext = class _FlowContext {
|
|
|
399
399
|
* - 输出仅来自 RunJS doc 与 defineProperty/defineMethod 的 info
|
|
400
400
|
* - 不读取/展开 PropertyMeta(变量结构)
|
|
401
401
|
* - 不自动展开深层 properties
|
|
402
|
-
* -
|
|
402
|
+
* - 默认不返回自动补全字段(例如 completion),传入 includeCompletion=true 时返回
|
|
403
403
|
*/
|
|
404
404
|
async getApiInfos(options = {}) {
|
|
405
405
|
const version = options.version || "v1";
|
|
406
|
+
const includeCompletion = !!options.includeCompletion;
|
|
406
407
|
const evalCtx = this.createProxy();
|
|
407
408
|
const isPrivateKey = /* @__PURE__ */ __name((key) => typeof key === "string" && key.startsWith("_"), "isPrivateKey");
|
|
408
409
|
const isVarRootKey = /* @__PURE__ */ __name((key) => key === "record" || key === "formValues" || key === "popup", "isVarRootKey");
|
|
@@ -439,7 +440,14 @@ const _FlowContext = class _FlowContext {
|
|
|
439
440
|
const src = toDocObject(obj);
|
|
440
441
|
if (!src) return {};
|
|
441
442
|
const out2 = {};
|
|
442
|
-
for (const k of [
|
|
443
|
+
for (const k of [
|
|
444
|
+
"description",
|
|
445
|
+
"examples",
|
|
446
|
+
...includeCompletion ? ["completion"] : [],
|
|
447
|
+
"ref",
|
|
448
|
+
"params",
|
|
449
|
+
"returns"
|
|
450
|
+
]) {
|
|
443
451
|
const v = src[k];
|
|
444
452
|
if (typeof v !== "undefined") out2[k] = v;
|
|
445
453
|
}
|
|
@@ -452,7 +460,17 @@ const _FlowContext = class _FlowContext {
|
|
|
452
460
|
const src = toDocObject(obj);
|
|
453
461
|
if (!src) return {};
|
|
454
462
|
const out2 = {};
|
|
455
|
-
for (const k of [
|
|
463
|
+
for (const k of [
|
|
464
|
+
"title",
|
|
465
|
+
"type",
|
|
466
|
+
"interface",
|
|
467
|
+
"description",
|
|
468
|
+
"examples",
|
|
469
|
+
...includeCompletion ? ["completion"] : [],
|
|
470
|
+
"ref",
|
|
471
|
+
"params",
|
|
472
|
+
"returns"
|
|
473
|
+
]) {
|
|
456
474
|
const v = src[k];
|
|
457
475
|
if (typeof v !== "undefined") out2[k] = v;
|
|
458
476
|
}
|
|
@@ -539,7 +557,7 @@ const _FlowContext = class _FlowContext {
|
|
|
539
557
|
node = { ...node, ...pickPropertyInfo(docObj) };
|
|
540
558
|
node = { ...node, ...pickPropertyInfo(infoObj) };
|
|
541
559
|
delete node.properties;
|
|
542
|
-
delete node.completion;
|
|
560
|
+
if (!includeCompletion) delete node.completion;
|
|
543
561
|
if (!Object.keys(node).length) continue;
|
|
544
562
|
const outKey = mapDocKeyToApiKey(key, docNode);
|
|
545
563
|
out[outKey] = out[outKey] ? { ...out[outKey] || {}, ...node || {} } : node;
|
|
@@ -554,7 +572,7 @@ const _FlowContext = class _FlowContext {
|
|
|
554
572
|
node = { ...node, ...pickMethodInfo(docObj) };
|
|
555
573
|
node = { ...node, ...pickMethodInfo(info) };
|
|
556
574
|
delete node.properties;
|
|
557
|
-
delete node.completion;
|
|
575
|
+
if (!includeCompletion) delete node.completion;
|
|
558
576
|
if (!Object.keys(node).length) continue;
|
|
559
577
|
node.type = "function";
|
|
560
578
|
if (!out[key]) out[key] = node;
|
|
@@ -571,7 +589,7 @@ const _FlowContext = class _FlowContext {
|
|
|
571
589
|
let node = {};
|
|
572
590
|
node = { ...node, ...pickPropertyInfo(childObj) };
|
|
573
591
|
delete node.properties;
|
|
574
|
-
delete node.completion;
|
|
592
|
+
if (!includeCompletion) delete node.completion;
|
|
575
593
|
if (!node.description || !String(node.description).trim()) continue;
|
|
576
594
|
out[outKey] = node;
|
|
577
595
|
}
|
|
@@ -2255,6 +2273,17 @@ const _BaseFlowEngineContext = class _BaseFlowEngineContext extends FlowContext
|
|
|
2255
2273
|
});
|
|
2256
2274
|
const jsCode = await (0, import_utils.prepareRunJsCode)(String(code ?? ""), { preprocessTemplates: shouldPreprocessTemplates });
|
|
2257
2275
|
return runner.run(jsCode);
|
|
2276
|
+
},
|
|
2277
|
+
{
|
|
2278
|
+
description: "Execute a RunJS code string in the current Flow context.",
|
|
2279
|
+
detail: "(code: string, variables?: Record<string, any>, options?: JSRunnerOptions) => Promise<RunJSResult>",
|
|
2280
|
+
params: [
|
|
2281
|
+
{ name: "code", type: "string", description: "RunJS code to execute." },
|
|
2282
|
+
{ name: "variables", type: "Record<string, any>", optional: true, description: "Additional globals." },
|
|
2283
|
+
{ name: "options", type: "JSRunnerOptions", optional: true, description: "Runner options." }
|
|
2284
|
+
],
|
|
2285
|
+
returns: { type: "Promise<{ success: boolean; value?: any; error?: any; timeout?: boolean }>" },
|
|
2286
|
+
completion: { insertText: `await ctx.runjs('return 1')` }
|
|
2258
2287
|
}
|
|
2259
2288
|
);
|
|
2260
2289
|
}
|
package/lib/flowEngine.d.ts
CHANGED
|
@@ -93,6 +93,7 @@ export declare class FlowEngine {
|
|
|
93
93
|
* @private
|
|
94
94
|
*/
|
|
95
95
|
private _savingModels;
|
|
96
|
+
private _loadedPageCache;
|
|
96
97
|
/**
|
|
97
98
|
* Flow engine context object.
|
|
98
99
|
* @private
|
|
@@ -537,11 +538,11 @@ export declare class FlowEngine {
|
|
|
537
538
|
replaceModel<T extends FlowModel = FlowModel>(uid: string, optionsOrFn?: Partial<FlowModelOptions> | ((currentOptions: FlowModelOptions) => Partial<FlowModelOptions>)): Promise<T | null>;
|
|
538
539
|
/**
|
|
539
540
|
* Move a model instance within its parent model.
|
|
540
|
-
* @param {
|
|
541
|
-
* @param {
|
|
541
|
+
* @param {string | number} sourceId Source model UID
|
|
542
|
+
* @param {string | number} targetId Target model UID
|
|
542
543
|
* @returns {Promise<void>} No return value
|
|
543
544
|
*/
|
|
544
|
-
moveModel(sourceId:
|
|
545
|
+
moveModel(sourceId: string | number, targetId: string | number, options?: PersistOptions): Promise<void>;
|
|
545
546
|
/**
|
|
546
547
|
* Filter model classes by parent class (supports multi-level inheritance).
|
|
547
548
|
* @param {string | ModelConstructor} parentClass Parent class name or constructor
|
package/lib/flowEngine.js
CHANGED
|
@@ -61,6 +61,7 @@ var import_ReactView = require("./ReactView");
|
|
|
61
61
|
var import_resources = require("./resources");
|
|
62
62
|
var import_emitter = require("./emitter");
|
|
63
63
|
var import_ModelOperationScheduler = __toESM(require("./scheduler/ModelOperationScheduler"));
|
|
64
|
+
var import_loadedPageCache = require("./utils/loadedPageCache");
|
|
64
65
|
var import_utils = require("./utils");
|
|
65
66
|
var _FlowEngine_instances, registerModel_fn;
|
|
66
67
|
const getFlowEngineLoggerLevel = /* @__PURE__ */ __name(() => process.env.NODE_ENV === "production" ? "warn" : "trace", "getFlowEngineLoggerLevel");
|
|
@@ -130,6 +131,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
130
131
|
* @private
|
|
131
132
|
*/
|
|
132
133
|
__publicField(this, "_savingModels", /* @__PURE__ */ new Map());
|
|
134
|
+
__publicField(this, "_loadedPageCache", (0, import_loadedPageCache.createLoadedPageCache)());
|
|
133
135
|
/**
|
|
134
136
|
* Flow engine context object.
|
|
135
137
|
* @private
|
|
@@ -1138,10 +1140,11 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1138
1140
|
async loadModel(options) {
|
|
1139
1141
|
if (!this.ensureModelRepository()) return;
|
|
1140
1142
|
const refresh = !!(options == null ? void 0 : options.refresh);
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1143
|
+
const bypassLoadedPageCache = this._loadedPageCache.shouldBypass(options, () => this.context.flowSettingsEnabled);
|
|
1144
|
+
if (!refresh && !bypassLoadedPageCache) {
|
|
1145
|
+
const model2 = this.findModelByParentId(options.parentId, options.subKey);
|
|
1146
|
+
if (model2) {
|
|
1147
|
+
return model2;
|
|
1145
1148
|
}
|
|
1146
1149
|
const hydrated = await this.hydrateModelFromPreviousEngines(options);
|
|
1147
1150
|
if (hydrated) {
|
|
@@ -1149,15 +1152,24 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1149
1152
|
}
|
|
1150
1153
|
}
|
|
1151
1154
|
const data = await this._modelRepository.findOne(options);
|
|
1152
|
-
if (!(data == null ? void 0 : data.uid))
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
+
if (!(data == null ? void 0 : data.uid)) {
|
|
1156
|
+
if (bypassLoadedPageCache) {
|
|
1157
|
+
this._loadedPageCache.clear(options);
|
|
1158
|
+
}
|
|
1159
|
+
return null;
|
|
1160
|
+
}
|
|
1161
|
+
if (refresh || bypassLoadedPageCache) {
|
|
1155
1162
|
const existing = this.getModel(data.uid);
|
|
1156
1163
|
if (existing) {
|
|
1157
1164
|
this.removeModelWithSubModels(existing.uid);
|
|
1158
1165
|
}
|
|
1159
1166
|
}
|
|
1160
|
-
|
|
1167
|
+
const model = await this.createModelAsync(data);
|
|
1168
|
+
if (bypassLoadedPageCache) {
|
|
1169
|
+
this._loadedPageCache.mountModelToParent(model, true);
|
|
1170
|
+
this._loadedPageCache.clear(options);
|
|
1171
|
+
}
|
|
1172
|
+
return model;
|
|
1161
1173
|
}
|
|
1162
1174
|
/**
|
|
1163
1175
|
* Find a sub-model by parent model ID and subKey.
|
|
@@ -1188,20 +1200,29 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1188
1200
|
async loadOrCreateModel(options, extra) {
|
|
1189
1201
|
if (!this.ensureModelRepository()) return;
|
|
1190
1202
|
const { uid, parentId, subKey } = options;
|
|
1191
|
-
|
|
1203
|
+
const bypassLoadedPageCache = this._loadedPageCache.shouldBypass(options, () => this.context.flowSettingsEnabled);
|
|
1204
|
+
if (uid && !bypassLoadedPageCache && this._modelInstances.has(uid)) {
|
|
1192
1205
|
return this._modelInstances.get(uid);
|
|
1193
1206
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1207
|
+
if (!bypassLoadedPageCache) {
|
|
1208
|
+
const m = this.findModelByParentId(parentId, subKey);
|
|
1209
|
+
if (m) {
|
|
1210
|
+
return m;
|
|
1211
|
+
}
|
|
1212
|
+
const hydrated = await this.hydrateModelFromPreviousEngines(options, extra);
|
|
1213
|
+
if (hydrated) {
|
|
1214
|
+
return hydrated;
|
|
1215
|
+
}
|
|
1201
1216
|
}
|
|
1202
1217
|
const data = await this._modelRepository.findOne(options);
|
|
1203
1218
|
let model = null;
|
|
1204
1219
|
if (data == null ? void 0 : data.uid) {
|
|
1220
|
+
if (bypassLoadedPageCache) {
|
|
1221
|
+
const existing = this.getModel(data.uid);
|
|
1222
|
+
if (existing) {
|
|
1223
|
+
this.removeModelWithSubModels(existing.uid);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1205
1226
|
model = await this.createModelAsync(data, extra);
|
|
1206
1227
|
} else {
|
|
1207
1228
|
model = await this.createModelAsync(options, extra);
|
|
@@ -1209,18 +1230,9 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1209
1230
|
await model.save();
|
|
1210
1231
|
}
|
|
1211
1232
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
});
|
|
1216
|
-
if (subModel) {
|
|
1217
|
-
return model;
|
|
1218
|
-
}
|
|
1219
|
-
if (model.subType === "array") {
|
|
1220
|
-
model.parent.addSubModel(model.subKey, model);
|
|
1221
|
-
} else {
|
|
1222
|
-
model.parent.setSubModel(model.subKey, model);
|
|
1223
|
-
}
|
|
1233
|
+
this._loadedPageCache.mountModelToParent(model, bypassLoadedPageCache);
|
|
1234
|
+
if (bypassLoadedPageCache) {
|
|
1235
|
+
this._loadedPageCache.clear(options);
|
|
1224
1236
|
}
|
|
1225
1237
|
return model;
|
|
1226
1238
|
}
|
|
@@ -1238,6 +1250,9 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1238
1250
|
async saveModel(model, options) {
|
|
1239
1251
|
if (!this.ensureModelRepository()) return;
|
|
1240
1252
|
const modelUid = model.uid;
|
|
1253
|
+
const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(model, {
|
|
1254
|
+
force: !!(options == null ? void 0 : options.onlyStepParams)
|
|
1255
|
+
});
|
|
1241
1256
|
if (this._savingModels.has(modelUid)) {
|
|
1242
1257
|
this.logger.debug(`Model ${modelUid} is already being saved, waiting for existing save operation`);
|
|
1243
1258
|
return await this._savingModels.get(modelUid);
|
|
@@ -1246,6 +1261,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1246
1261
|
this._savingModels.set(modelUid, savePromise);
|
|
1247
1262
|
try {
|
|
1248
1263
|
const result = await savePromise;
|
|
1264
|
+
this._loadedPageCache.markDirty(dirtyLoadedPageKey);
|
|
1249
1265
|
return result;
|
|
1250
1266
|
} finally {
|
|
1251
1267
|
this._savingModels.delete(modelUid);
|
|
@@ -1276,10 +1292,15 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1276
1292
|
* @returns {Promise<boolean>} Whether destroyed successfully
|
|
1277
1293
|
*/
|
|
1278
1294
|
async destroyModel(uid) {
|
|
1279
|
-
|
|
1295
|
+
const modelInstance = this._modelInstances.get(uid);
|
|
1296
|
+
const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(modelInstance);
|
|
1297
|
+
const hasModelRepository = this.ensureModelRepository();
|
|
1298
|
+
if (hasModelRepository) {
|
|
1280
1299
|
await this._modelRepository.destroy(uid);
|
|
1281
1300
|
}
|
|
1282
|
-
|
|
1301
|
+
if (hasModelRepository) {
|
|
1302
|
+
this._loadedPageCache.markDirty(dirtyLoadedPageKey);
|
|
1303
|
+
}
|
|
1283
1304
|
const parent = modelInstance == null ? void 0 : modelInstance.parent;
|
|
1284
1305
|
const result = this.removeModel(uid);
|
|
1285
1306
|
parent && parent.emitter.emit("onSubModelDestroyed", modelInstance);
|
|
@@ -1355,18 +1376,25 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1355
1376
|
}
|
|
1356
1377
|
/**
|
|
1357
1378
|
* Move a model instance within its parent model.
|
|
1358
|
-
* @param {
|
|
1359
|
-
* @param {
|
|
1379
|
+
* @param {string | number} sourceId Source model UID
|
|
1380
|
+
* @param {string | number} targetId Target model UID
|
|
1360
1381
|
* @returns {Promise<void>} No return value
|
|
1361
1382
|
*/
|
|
1362
1383
|
async moveModel(sourceId, targetId, options) {
|
|
1363
1384
|
var _a, _b;
|
|
1364
|
-
const
|
|
1365
|
-
const
|
|
1385
|
+
const sourceUid = String(sourceId);
|
|
1386
|
+
const targetUid = String(targetId);
|
|
1387
|
+
if (!sourceUid || !targetUid || sourceUid === targetUid) {
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
const sourceModel = this.getModel(sourceUid);
|
|
1391
|
+
const targetModel = this.getModel(targetUid);
|
|
1366
1392
|
if (!sourceModel || !targetModel) {
|
|
1367
1393
|
console.warn(`FlowEngine: Cannot move model. Source or target model not found.`);
|
|
1368
1394
|
return;
|
|
1369
1395
|
}
|
|
1396
|
+
let position = "after";
|
|
1397
|
+
const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(sourceModel);
|
|
1370
1398
|
const move = /* @__PURE__ */ __name((sourceModel2, targetModel2) => {
|
|
1371
1399
|
if (!sourceModel2.parent || !targetModel2.parent || sourceModel2.parent !== targetModel2.parent) {
|
|
1372
1400
|
console.error("FlowModel.moveTo: Both models must have the same parent to perform move operation.");
|
|
@@ -1389,6 +1417,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1389
1417
|
console.warn("FlowModel.moveTo: Current model is already at the target position. No action taken.");
|
|
1390
1418
|
return false;
|
|
1391
1419
|
}
|
|
1420
|
+
position = currentIndex < targetIndex ? "after" : "before";
|
|
1392
1421
|
const [movedModel] = subModelsCopy.splice(currentIndex, 1);
|
|
1393
1422
|
subModelsCopy.splice(targetIndex, 0, movedModel);
|
|
1394
1423
|
subModelsCopy.forEach((model, index) => {
|
|
@@ -1397,10 +1426,13 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1397
1426
|
subModels.splice(0, subModels.length, ...subModelsCopy);
|
|
1398
1427
|
return true;
|
|
1399
1428
|
}, "move");
|
|
1400
|
-
move(sourceModel, targetModel);
|
|
1429
|
+
const moved = move(sourceModel, targetModel);
|
|
1430
|
+
if (!moved) {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1401
1433
|
if ((options == null ? void 0 : options.persist) !== false && this.ensureModelRepository()) {
|
|
1402
|
-
|
|
1403
|
-
|
|
1434
|
+
await this._modelRepository.move(sourceUid, targetUid, position);
|
|
1435
|
+
this._loadedPageCache.markDirty(dirtyLoadedPageKey);
|
|
1404
1436
|
}
|
|
1405
1437
|
sourceModel.parent.emitter.emit("onSubModelMoved", { source: sourceModel, target: targetModel });
|
|
1406
1438
|
(_b = this.emitter) == null ? void 0 : _b.emit("model:subModel:moved", {
|