@nocobase/flow-engine 2.0.59 → 2.0.61
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/flowContext.d.ts +6 -1
- package/lib/flowContext.js +35 -6
- package/lib/flowEngine.d.ts +4 -3
- package/lib/flowEngine.js +67 -36
- 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 +21 -0
- package/lib/utils/loadedPageCache.js +125 -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 +148 -1
- package/src/flowContext.ts +40 -6
- package/src/flowEngine.ts +69 -34
- package/src/models/__tests__/flowModel.test.ts +13 -0
- 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 +117 -0
|
@@ -184,8 +184,17 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
184
184
|
const [visible, setVisible] = (0, import_react.useState)(false);
|
|
185
185
|
const [refreshTick, setRefreshTick] = (0, import_react.useState)(0);
|
|
186
186
|
const [extraMenuItems, setExtraMenuItems] = (0, import_react.useState)([]);
|
|
187
|
+
const [extraMenuItemsLoaded, setExtraMenuItemsLoaded] = (0, import_react.useState)(false);
|
|
187
188
|
const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = (0, import_react.useState)([]);
|
|
188
189
|
const [isLoading, setIsLoading] = (0, import_react.useState)(true);
|
|
190
|
+
const commonExtras = (0, import_react.useMemo)(
|
|
191
|
+
() => extraMenuItems.filter((it) => it.group === "common-actions").sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)),
|
|
192
|
+
[extraMenuItems]
|
|
193
|
+
);
|
|
194
|
+
const hasCommonActions = showCopyUidButton || showDeleteButton || commonExtras.length > 0;
|
|
195
|
+
const shouldDeferConfigLoading = flattenSubMenus && menuLevels > 1 && hasCommonActions;
|
|
196
|
+
const shouldWaitForCommonActionProbe = flattenSubMenus && menuLevels > 1 && !showCopyUidButton && !showDeleteButton && !extraMenuItemsLoaded;
|
|
197
|
+
const canRenderIcon = hasCommonActions || !isLoading && configurableFlowsAndSteps.length > 0;
|
|
189
198
|
const closeDropdown = (0, import_react.useCallback)(() => {
|
|
190
199
|
setVisible(false);
|
|
191
200
|
onDropdownVisibleChange == null ? void 0 : onDropdownVisibleChange(false);
|
|
@@ -217,24 +226,28 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
217
226
|
let mounted = true;
|
|
218
227
|
const loadExtras = /* @__PURE__ */ __name(async () => {
|
|
219
228
|
var _a;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
modelsToProcess
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
229
|
+
setExtraMenuItemsLoaded(false);
|
|
230
|
+
try {
|
|
231
|
+
const allExtras = [];
|
|
232
|
+
const modelsToProcess = [];
|
|
233
|
+
walkSubModels(model, { maxDepth: menuLevels, arrayLimit: 50, mode: "stack" }, (targetModel, { modelKey }) => {
|
|
234
|
+
modelsToProcess.push({ model: targetModel, modelKey });
|
|
235
|
+
});
|
|
236
|
+
for (const { model: targetModel, modelKey } of modelsToProcess) {
|
|
237
|
+
const Cls = targetModel.constructor;
|
|
238
|
+
const extras = await ((_a = Cls.getExtraMenuItems) == null ? void 0 : _a.call(Cls, targetModel, t));
|
|
239
|
+
if (extras == null ? void 0 : extras.length) {
|
|
240
|
+
allExtras.push(
|
|
241
|
+
...extras.map((item) => ({
|
|
242
|
+
...item,
|
|
243
|
+
key: modelKey ? `${modelKey}:${item.key}` : item.key
|
|
244
|
+
}))
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (!mounted) {
|
|
249
|
+
return;
|
|
235
250
|
}
|
|
236
|
-
}
|
|
237
|
-
if (mounted) {
|
|
238
251
|
const seen = /* @__PURE__ */ new Set();
|
|
239
252
|
const dedupedExtras = allExtras.filter((item) => {
|
|
240
253
|
if (seen.has(`${item.key}`)) {
|
|
@@ -244,15 +257,22 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
244
257
|
return true;
|
|
245
258
|
});
|
|
246
259
|
setExtraMenuItems(dedupedExtras);
|
|
260
|
+
} catch (error) {
|
|
261
|
+
console.error("Failed to load extra menu items:", error);
|
|
262
|
+
if (mounted) {
|
|
263
|
+
setExtraMenuItems([]);
|
|
264
|
+
}
|
|
265
|
+
} finally {
|
|
266
|
+
if (mounted) {
|
|
267
|
+
setExtraMenuItemsLoaded(true);
|
|
268
|
+
}
|
|
247
269
|
}
|
|
248
270
|
}, "loadExtras");
|
|
249
|
-
|
|
250
|
-
loadExtras();
|
|
251
|
-
}
|
|
271
|
+
loadExtras();
|
|
252
272
|
return () => {
|
|
253
273
|
mounted = false;
|
|
254
274
|
};
|
|
255
|
-
}, [model, menuLevels, t, refreshTick
|
|
275
|
+
}, [model, menuLevels, t, refreshTick]);
|
|
256
276
|
const copyUidToClipboard = (0, import_react.useCallback)(
|
|
257
277
|
async (uid) => {
|
|
258
278
|
var _a;
|
|
@@ -494,7 +514,7 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
494
514
|
return [];
|
|
495
515
|
}
|
|
496
516
|
},
|
|
497
|
-
[]
|
|
517
|
+
[t]
|
|
498
518
|
);
|
|
499
519
|
const getConfigurableFlowsAndSteps = (0, import_react.useCallback)(async () => {
|
|
500
520
|
const result = [];
|
|
@@ -532,20 +552,47 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
532
552
|
};
|
|
533
553
|
}, [model, menuLevels, refreshTick]);
|
|
534
554
|
(0, import_react.useEffect)(() => {
|
|
555
|
+
let mounted = true;
|
|
535
556
|
const loadConfigurableFlowsAndSteps = /* @__PURE__ */ __name(async () => {
|
|
536
557
|
setIsLoading(true);
|
|
558
|
+
if (shouldDeferConfigLoading) {
|
|
559
|
+
setConfigurableFlowsAndSteps([]);
|
|
560
|
+
}
|
|
537
561
|
try {
|
|
538
562
|
const flows = await getConfigurableFlowsAndSteps();
|
|
539
|
-
|
|
563
|
+
if (mounted) {
|
|
564
|
+
setConfigurableFlowsAndSteps(flows);
|
|
565
|
+
}
|
|
540
566
|
} catch (error) {
|
|
541
567
|
console.error("Failed to load configurable flows and steps:", error);
|
|
542
|
-
|
|
568
|
+
if (mounted) {
|
|
569
|
+
setConfigurableFlowsAndSteps([]);
|
|
570
|
+
}
|
|
543
571
|
} finally {
|
|
544
|
-
|
|
572
|
+
if (mounted) {
|
|
573
|
+
setIsLoading(false);
|
|
574
|
+
}
|
|
545
575
|
}
|
|
546
576
|
}, "loadConfigurableFlowsAndSteps");
|
|
577
|
+
if (shouldWaitForCommonActionProbe) {
|
|
578
|
+
setConfigurableFlowsAndSteps([]);
|
|
579
|
+
setIsLoading(false);
|
|
580
|
+
return () => {
|
|
581
|
+
mounted = false;
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
if (!visible && shouldDeferConfigLoading) {
|
|
585
|
+
setConfigurableFlowsAndSteps([]);
|
|
586
|
+
setIsLoading(false);
|
|
587
|
+
return () => {
|
|
588
|
+
mounted = false;
|
|
589
|
+
};
|
|
590
|
+
}
|
|
547
591
|
loadConfigurableFlowsAndSteps();
|
|
548
|
-
|
|
592
|
+
return () => {
|
|
593
|
+
mounted = false;
|
|
594
|
+
};
|
|
595
|
+
}, [getConfigurableFlowsAndSteps, refreshTick, shouldDeferConfigLoading, shouldWaitForCommonActionProbe, visible]);
|
|
549
596
|
const menuItems = (0, import_react.useMemo)(() => {
|
|
550
597
|
const items = [];
|
|
551
598
|
const keyCounter = /* @__PURE__ */ new Map();
|
|
@@ -670,10 +717,9 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
670
717
|
}
|
|
671
718
|
}
|
|
672
719
|
return items;
|
|
673
|
-
}, [configurableFlowsAndSteps, disabledIconColor, flattenSubMenus, t]);
|
|
720
|
+
}, [configurableFlowsAndSteps, disabledIconColor, flattenSubMenus, message, model, t]);
|
|
674
721
|
const finalMenuItems = (0, import_react.useMemo)(() => {
|
|
675
722
|
const items = [...menuItems];
|
|
676
|
-
const commonExtras = extraMenuItems.filter((it) => it.group === "common-actions").sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
|
|
677
723
|
if (showCopyUidButton || showDeleteButton || commonExtras.length > 0) {
|
|
678
724
|
items.push({
|
|
679
725
|
type: "divider"
|
|
@@ -695,9 +741,8 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
|
|
|
695
741
|
}
|
|
696
742
|
}
|
|
697
743
|
return items;
|
|
698
|
-
}, [menuItems, showCopyUidButton, showDeleteButton, model.uid, model.destroy, t
|
|
699
|
-
|
|
700
|
-
if (isLoading || configurableFlowsAndSteps.length === 0 && !showDeleteButton && !showCopyUidButton && !hasExtras) {
|
|
744
|
+
}, [menuItems, showCopyUidButton, showDeleteButton, commonExtras, model.uid, model.destroy, t]);
|
|
745
|
+
if (!canRenderIcon) {
|
|
701
746
|
return null;
|
|
702
747
|
}
|
|
703
748
|
if (!model || !model.uid) {
|
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
|
/**
|
|
@@ -421,6 +425,7 @@ export declare class FlowRuntimeContext<TModel extends FlowModel = FlowModel, TM
|
|
|
421
425
|
export type FlowSettingsContext<TModel extends FlowModel = FlowModel> = FlowRuntimeContext<TModel, 'settings'>;
|
|
422
426
|
export type RunJSDocCompletionDoc = {
|
|
423
427
|
insertText?: string;
|
|
428
|
+
requires?: Array<'element'>;
|
|
424
429
|
};
|
|
425
430
|
export type RunJSDocHiddenDoc = boolean | ((ctx: any) => boolean | Promise<boolean>);
|
|
426
431
|
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
|
@@ -71,6 +71,7 @@ export declare class FlowEngine {
|
|
|
71
71
|
* @private
|
|
72
72
|
*/
|
|
73
73
|
private _savingModels;
|
|
74
|
+
private _loadedPageCache;
|
|
74
75
|
/**
|
|
75
76
|
* Flow engine context object.
|
|
76
77
|
* @private
|
|
@@ -406,11 +407,11 @@ export declare class FlowEngine {
|
|
|
406
407
|
replaceModel<T extends FlowModel = FlowModel>(uid: string, optionsOrFn?: Partial<FlowModelOptions> | ((currentOptions: FlowModelOptions) => Partial<FlowModelOptions>)): Promise<T | null>;
|
|
407
408
|
/**
|
|
408
409
|
* Move a model instance within its parent model.
|
|
409
|
-
* @param {
|
|
410
|
-
* @param {
|
|
410
|
+
* @param {string | number} sourceId Source model UID
|
|
411
|
+
* @param {string | number} targetId Target model UID
|
|
411
412
|
* @returns {Promise<void>} No return value
|
|
412
413
|
*/
|
|
413
|
-
moveModel(sourceId:
|
|
414
|
+
moveModel(sourceId: string | number, targetId: string | number, options?: PersistOptions): Promise<void>;
|
|
414
415
|
/**
|
|
415
416
|
* Filter model classes by parent class (supports multi-level inheritance).
|
|
416
417
|
* @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");
|
|
@@ -108,6 +109,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
108
109
|
* @private
|
|
109
110
|
*/
|
|
110
111
|
__publicField(this, "_savingModels", /* @__PURE__ */ new Map());
|
|
112
|
+
__publicField(this, "_loadedPageCache", (0, import_loadedPageCache.createLoadedPageCache)());
|
|
111
113
|
/**
|
|
112
114
|
* Flow engine context object.
|
|
113
115
|
* @private
|
|
@@ -789,10 +791,11 @@ const _FlowEngine = class _FlowEngine {
|
|
|
789
791
|
async loadModel(options) {
|
|
790
792
|
if (!this.ensureModelRepository()) return;
|
|
791
793
|
const refresh = !!(options == null ? void 0 : options.refresh);
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
794
|
+
const bypassLoadedPageCache = this._loadedPageCache.shouldBypass(options, () => this.context.flowSettingsEnabled);
|
|
795
|
+
if (!refresh && !bypassLoadedPageCache) {
|
|
796
|
+
const model2 = this.findModelByParentId(options.parentId, options.subKey);
|
|
797
|
+
if (model2) {
|
|
798
|
+
return model2;
|
|
796
799
|
}
|
|
797
800
|
const hydrated = this.hydrateModelFromPreviousEngines(options);
|
|
798
801
|
if (hydrated) {
|
|
@@ -800,14 +803,24 @@ const _FlowEngine = class _FlowEngine {
|
|
|
800
803
|
}
|
|
801
804
|
}
|
|
802
805
|
const data = await this._modelRepository.findOne(options);
|
|
803
|
-
if (!(data == null ? void 0 : data.uid))
|
|
804
|
-
|
|
806
|
+
if (!(data == null ? void 0 : data.uid)) {
|
|
807
|
+
if (bypassLoadedPageCache) {
|
|
808
|
+
this._loadedPageCache.clear(options);
|
|
809
|
+
}
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
if (refresh || bypassLoadedPageCache) {
|
|
805
813
|
const existing = this.getModel(data.uid);
|
|
806
814
|
if (existing) {
|
|
807
815
|
this.removeModelWithSubModels(existing.uid);
|
|
808
816
|
}
|
|
809
817
|
}
|
|
810
|
-
|
|
818
|
+
const model = this.createModel(data);
|
|
819
|
+
if (bypassLoadedPageCache) {
|
|
820
|
+
this._loadedPageCache.mountModelToParent(model, true);
|
|
821
|
+
this._loadedPageCache.clear(options);
|
|
822
|
+
}
|
|
823
|
+
return model;
|
|
811
824
|
}
|
|
812
825
|
/**
|
|
813
826
|
* Find a sub-model by parent model ID and subKey.
|
|
@@ -838,20 +851,29 @@ const _FlowEngine = class _FlowEngine {
|
|
|
838
851
|
async loadOrCreateModel(options, extra) {
|
|
839
852
|
if (!this.ensureModelRepository()) return;
|
|
840
853
|
const { uid, parentId, subKey } = options;
|
|
841
|
-
|
|
854
|
+
const bypassLoadedPageCache = this._loadedPageCache.shouldBypass(options, () => this.context.flowSettingsEnabled);
|
|
855
|
+
if (uid && !bypassLoadedPageCache && this._modelInstances.has(uid)) {
|
|
842
856
|
return this._modelInstances.get(uid);
|
|
843
857
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
858
|
+
if (!bypassLoadedPageCache) {
|
|
859
|
+
const m = this.findModelByParentId(parentId, subKey);
|
|
860
|
+
if (m) {
|
|
861
|
+
return m;
|
|
862
|
+
}
|
|
863
|
+
const hydrated = this.hydrateModelFromPreviousEngines(options, extra);
|
|
864
|
+
if (hydrated) {
|
|
865
|
+
return hydrated;
|
|
866
|
+
}
|
|
851
867
|
}
|
|
852
868
|
const data = await this._modelRepository.findOne(options);
|
|
853
869
|
let model = null;
|
|
854
870
|
if (data == null ? void 0 : data.uid) {
|
|
871
|
+
if (bypassLoadedPageCache) {
|
|
872
|
+
const existing = this.getModel(data.uid);
|
|
873
|
+
if (existing) {
|
|
874
|
+
this.removeModelWithSubModels(existing.uid);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
855
877
|
model = this.createModel(data, extra);
|
|
856
878
|
} else {
|
|
857
879
|
model = this.createModel(options, extra);
|
|
@@ -859,18 +881,9 @@ const _FlowEngine = class _FlowEngine {
|
|
|
859
881
|
await model.save();
|
|
860
882
|
}
|
|
861
883
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
});
|
|
866
|
-
if (subModel) {
|
|
867
|
-
return model;
|
|
868
|
-
}
|
|
869
|
-
if (model.subType === "array") {
|
|
870
|
-
model.parent.addSubModel(model.subKey, model);
|
|
871
|
-
} else {
|
|
872
|
-
model.parent.setSubModel(model.subKey, model);
|
|
873
|
-
}
|
|
884
|
+
this._loadedPageCache.mountModelToParent(model, bypassLoadedPageCache);
|
|
885
|
+
if (bypassLoadedPageCache) {
|
|
886
|
+
this._loadedPageCache.clear(options);
|
|
874
887
|
}
|
|
875
888
|
return model;
|
|
876
889
|
}
|
|
@@ -888,6 +901,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
888
901
|
async saveModel(model, options) {
|
|
889
902
|
if (!this.ensureModelRepository()) return;
|
|
890
903
|
const modelUid = model.uid;
|
|
904
|
+
const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(model);
|
|
891
905
|
if (this._savingModels.has(modelUid)) {
|
|
892
906
|
this.logger.debug(`Model ${modelUid} is already being saved, waiting for existing save operation`);
|
|
893
907
|
return await this._savingModels.get(modelUid);
|
|
@@ -896,6 +910,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
896
910
|
this._savingModels.set(modelUid, savePromise);
|
|
897
911
|
try {
|
|
898
912
|
const result = await savePromise;
|
|
913
|
+
this._loadedPageCache.markDirty(dirtyLoadedPageKey);
|
|
899
914
|
return result;
|
|
900
915
|
} finally {
|
|
901
916
|
this._savingModels.delete(modelUid);
|
|
@@ -926,10 +941,15 @@ const _FlowEngine = class _FlowEngine {
|
|
|
926
941
|
* @returns {Promise<boolean>} Whether destroyed successfully
|
|
927
942
|
*/
|
|
928
943
|
async destroyModel(uid) {
|
|
929
|
-
|
|
944
|
+
const modelInstance = this._modelInstances.get(uid);
|
|
945
|
+
const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(modelInstance);
|
|
946
|
+
const hasModelRepository = this.ensureModelRepository();
|
|
947
|
+
if (hasModelRepository) {
|
|
930
948
|
await this._modelRepository.destroy(uid);
|
|
931
949
|
}
|
|
932
|
-
|
|
950
|
+
if (hasModelRepository) {
|
|
951
|
+
this._loadedPageCache.markDirty(dirtyLoadedPageKey);
|
|
952
|
+
}
|
|
933
953
|
const parent = modelInstance == null ? void 0 : modelInstance.parent;
|
|
934
954
|
const result = this.removeModel(uid);
|
|
935
955
|
parent && parent.emitter.emit("onSubModelDestroyed", modelInstance);
|
|
@@ -1005,18 +1025,25 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1005
1025
|
}
|
|
1006
1026
|
/**
|
|
1007
1027
|
* Move a model instance within its parent model.
|
|
1008
|
-
* @param {
|
|
1009
|
-
* @param {
|
|
1028
|
+
* @param {string | number} sourceId Source model UID
|
|
1029
|
+
* @param {string | number} targetId Target model UID
|
|
1010
1030
|
* @returns {Promise<void>} No return value
|
|
1011
1031
|
*/
|
|
1012
1032
|
async moveModel(sourceId, targetId, options) {
|
|
1013
1033
|
var _a, _b;
|
|
1014
|
-
const
|
|
1015
|
-
const
|
|
1034
|
+
const sourceUid = String(sourceId);
|
|
1035
|
+
const targetUid = String(targetId);
|
|
1036
|
+
if (!sourceUid || !targetUid || sourceUid === targetUid) {
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
const sourceModel = this.getModel(sourceUid);
|
|
1040
|
+
const targetModel = this.getModel(targetUid);
|
|
1016
1041
|
if (!sourceModel || !targetModel) {
|
|
1017
1042
|
console.warn(`FlowEngine: Cannot move model. Source or target model not found.`);
|
|
1018
1043
|
return;
|
|
1019
1044
|
}
|
|
1045
|
+
let position = "after";
|
|
1046
|
+
const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(sourceModel);
|
|
1020
1047
|
const move = /* @__PURE__ */ __name((sourceModel2, targetModel2) => {
|
|
1021
1048
|
if (!sourceModel2.parent || !targetModel2.parent || sourceModel2.parent !== targetModel2.parent) {
|
|
1022
1049
|
console.error("FlowModel.moveTo: Both models must have the same parent to perform move operation.");
|
|
@@ -1039,6 +1066,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1039
1066
|
console.warn("FlowModel.moveTo: Current model is already at the target position. No action taken.");
|
|
1040
1067
|
return false;
|
|
1041
1068
|
}
|
|
1069
|
+
position = currentIndex < targetIndex ? "after" : "before";
|
|
1042
1070
|
const [movedModel] = subModelsCopy.splice(currentIndex, 1);
|
|
1043
1071
|
subModelsCopy.splice(targetIndex, 0, movedModel);
|
|
1044
1072
|
subModelsCopy.forEach((model, index) => {
|
|
@@ -1047,10 +1075,13 @@ const _FlowEngine = class _FlowEngine {
|
|
|
1047
1075
|
subModels.splice(0, subModels.length, ...subModelsCopy);
|
|
1048
1076
|
return true;
|
|
1049
1077
|
}, "move");
|
|
1050
|
-
move(sourceModel, targetModel);
|
|
1078
|
+
const moved = move(sourceModel, targetModel);
|
|
1079
|
+
if (!moved) {
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1051
1082
|
if ((options == null ? void 0 : options.persist) !== false && this.ensureModelRepository()) {
|
|
1052
|
-
|
|
1053
|
-
|
|
1083
|
+
await this._modelRepository.move(sourceUid, targetUid, position);
|
|
1084
|
+
this._loadedPageCache.markDirty(dirtyLoadedPageKey);
|
|
1054
1085
|
}
|
|
1055
1086
|
sourceModel.parent.emitter.emit("onSubModelMoved", { source: sourceModel, target: targetModel });
|
|
1056
1087
|
(_b = this.emitter) == null ? void 0 : _b.emit("model:subModel:moved", {
|
package/lib/models/flowModel.js
CHANGED
|
@@ -73,6 +73,18 @@ var _flowContext;
|
|
|
73
73
|
const classActionRegistries = /* @__PURE__ */ new WeakMap();
|
|
74
74
|
const classEventRegistries = /* @__PURE__ */ new WeakMap();
|
|
75
75
|
const modelMetas = /* @__PURE__ */ new WeakMap();
|
|
76
|
+
function getStableSortIndex(item, fallbackIndex) {
|
|
77
|
+
return typeof (item == null ? void 0 : item.sortIndex) === "number" && Number.isFinite(item.sortIndex) ? item.sortIndex : fallbackIndex + 1;
|
|
78
|
+
}
|
|
79
|
+
__name(getStableSortIndex, "getStableSortIndex");
|
|
80
|
+
function sortByStableSortIndex(items) {
|
|
81
|
+
return items.map((item, index) => ({
|
|
82
|
+
item,
|
|
83
|
+
index,
|
|
84
|
+
sortIndex: getStableSortIndex(item, index)
|
|
85
|
+
})).sort((a, b) => a.sortIndex - b.sortIndex || a.index - b.index).map(({ item }) => item);
|
|
86
|
+
}
|
|
87
|
+
__name(sortByStableSortIndex, "sortByStableSortIndex");
|
|
76
88
|
const modelGlobalRegistries = /* @__PURE__ */ new WeakMap();
|
|
77
89
|
const classMenuExtensions = /* @__PURE__ */ new WeakMap();
|
|
78
90
|
var ModelRenderMode = /* @__PURE__ */ ((ModelRenderMode2) => {
|
|
@@ -161,7 +173,7 @@ const _FlowModel = class _FlowModel {
|
|
|
161
173
|
};
|
|
162
174
|
this.stepParams = options.stepParams || {};
|
|
163
175
|
this.subModels = {};
|
|
164
|
-
this.sortIndex = options.sortIndex
|
|
176
|
+
this.sortIndex = getStableSortIndex({ sortIndex: options.sortIndex }, -1);
|
|
165
177
|
this._options = options;
|
|
166
178
|
this._title = "";
|
|
167
179
|
this._extraTitle = "";
|
|
@@ -373,7 +385,7 @@ const _FlowModel = class _FlowModel {
|
|
|
373
385
|
}
|
|
374
386
|
Object.entries(mergedSubModels || {}).forEach(([key, value]) => {
|
|
375
387
|
if (Array.isArray(value)) {
|
|
376
|
-
value
|
|
388
|
+
sortByStableSortIndex(value).forEach((item) => {
|
|
377
389
|
this.addSubModel(key, item);
|
|
378
390
|
});
|
|
379
391
|
} else {
|
|
@@ -584,26 +596,43 @@ const _FlowModel = class _FlowModel {
|
|
|
584
596
|
return this.props;
|
|
585
597
|
}
|
|
586
598
|
setStepParams(flowKeyOrAllParams, stepKeyOrStepsParams, params) {
|
|
599
|
+
var _a;
|
|
600
|
+
let hasChanged = false;
|
|
587
601
|
if (typeof flowKeyOrAllParams === "string") {
|
|
588
602
|
const flowKey = flowKeyOrAllParams;
|
|
589
603
|
if (typeof stepKeyOrStepsParams === "string" && params !== void 0) {
|
|
590
|
-
|
|
591
|
-
|
|
604
|
+
const currentStepParams = ((_a = this.stepParams[flowKey]) == null ? void 0 : _a[stepKeyOrStepsParams]) || {};
|
|
605
|
+
const nextStepParams = { ...currentStepParams, ...params };
|
|
606
|
+
if (!import_lodash.default.isEqual(currentStepParams, nextStepParams)) {
|
|
607
|
+
if (!this.stepParams[flowKey]) {
|
|
608
|
+
this.stepParams[flowKey] = {};
|
|
609
|
+
}
|
|
610
|
+
this.stepParams[flowKey][stepKeyOrStepsParams] = nextStepParams;
|
|
611
|
+
hasChanged = true;
|
|
592
612
|
}
|
|
593
|
-
this.stepParams[flowKey][stepKeyOrStepsParams] = {
|
|
594
|
-
...this.stepParams[flowKey][stepKeyOrStepsParams],
|
|
595
|
-
...params
|
|
596
|
-
};
|
|
597
613
|
} else if (typeof stepKeyOrStepsParams === "object" && stepKeyOrStepsParams !== null) {
|
|
598
|
-
|
|
614
|
+
const currentFlowParams = this.stepParams[flowKey] || {};
|
|
615
|
+
const nextFlowParams = { ...currentFlowParams, ...stepKeyOrStepsParams };
|
|
616
|
+
if (!import_lodash.default.isEqual(currentFlowParams, nextFlowParams)) {
|
|
617
|
+
this.stepParams[flowKey] = nextFlowParams;
|
|
618
|
+
hasChanged = true;
|
|
619
|
+
}
|
|
599
620
|
}
|
|
600
621
|
} else if (typeof flowKeyOrAllParams === "object" && flowKeyOrAllParams !== null) {
|
|
601
622
|
for (const fk in flowKeyOrAllParams) {
|
|
602
623
|
if (Object.prototype.hasOwnProperty.call(flowKeyOrAllParams, fk)) {
|
|
603
|
-
|
|
624
|
+
const currentFlowParams = this.stepParams[fk] || {};
|
|
625
|
+
const nextFlowParams = { ...currentFlowParams, ...flowKeyOrAllParams[fk] };
|
|
626
|
+
if (!import_lodash.default.isEqual(currentFlowParams, nextFlowParams)) {
|
|
627
|
+
this.stepParams[fk] = nextFlowParams;
|
|
628
|
+
hasChanged = true;
|
|
629
|
+
}
|
|
604
630
|
}
|
|
605
631
|
}
|
|
606
632
|
}
|
|
633
|
+
if (!hasChanged) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
607
636
|
this.emitter.emit("onStepParamsChanged");
|
|
608
637
|
}
|
|
609
638
|
getStepParams(flowKey, stepKey) {
|
|
@@ -889,7 +918,10 @@ const _FlowModel = class _FlowModel {
|
|
|
889
918
|
if (!Array.isArray(subModels[subKey])) {
|
|
890
919
|
subModels[subKey] = import_reactive.observable.shallow([]);
|
|
891
920
|
}
|
|
892
|
-
const maxSortIndex = Math.max(
|
|
921
|
+
const maxSortIndex = Math.max(
|
|
922
|
+
...subModels[subKey].map((item, index) => getStableSortIndex(item, index)),
|
|
923
|
+
0
|
|
924
|
+
);
|
|
893
925
|
model.sortIndex = maxSortIndex + 1;
|
|
894
926
|
subModels[subKey].push(model);
|
|
895
927
|
actualParent.emitter.emit("onSubModelAdded", model);
|
|
@@ -937,7 +969,7 @@ const _FlowModel = class _FlowModel {
|
|
|
937
969
|
return [];
|
|
938
970
|
}
|
|
939
971
|
const results = [];
|
|
940
|
-
import_lodash.default.castArray(model)
|
|
972
|
+
sortByStableSortIndex(import_lodash.default.castArray(model)).forEach((item, index) => {
|
|
941
973
|
const result = callback(item, index);
|
|
942
974
|
if (result) {
|
|
943
975
|
results.push(item);
|
|
@@ -951,7 +983,7 @@ const _FlowModel = class _FlowModel {
|
|
|
951
983
|
return [];
|
|
952
984
|
}
|
|
953
985
|
const results = [];
|
|
954
|
-
import_lodash.default.castArray(model)
|
|
986
|
+
sortByStableSortIndex(import_lodash.default.castArray(model)).forEach((item, index) => {
|
|
955
987
|
const result = callback(item, index);
|
|
956
988
|
results.push(result);
|
|
957
989
|
});
|