@nocobase/flow-engine 2.0.22 → 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/lib/components/MobilePopup.js +6 -5
- package/lib/components/subModel/AddSubModelButton.js +1 -1
- package/lib/components/subModel/utils.js +2 -2
- package/lib/flowEngine.d.ts +120 -1
- package/lib/flowEngine.js +301 -14
- package/lib/flowSettings.d.ts +14 -6
- package/lib/flowSettings.js +34 -6
- package/lib/lazy-helper.d.ts +14 -0
- package/lib/lazy-helper.js +71 -0
- package/lib/models/flowModel.js +17 -7
- package/lib/types.d.ts +35 -0
- package/lib/utils/runjsTemplateCompat.js +1 -1
- package/package.json +4 -4
- package/src/__tests__/flowEngine.modelLoaders.test.ts +245 -0
- package/src/__tests__/flowSettings.test.ts +94 -15
- package/src/__tests__/renderHiddenInConfig.test.tsx +6 -6
- package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
- package/src/components/MobilePopup.tsx +4 -2
- package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +3 -3
- package/src/components/subModel/AddSubModelButton.tsx +1 -1
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +93 -33
- package/src/components/subModel/utils.ts +1 -1
- package/src/flowEngine.ts +338 -10
- package/src/flowSettings.ts +40 -6
- package/src/lazy-helper.tsx +57 -0
- package/src/models/flowModel.tsx +18 -6
- package/src/types.ts +47 -0
- package/src/utils/runjsTemplateCompat.ts +1 -1
|
@@ -41,11 +41,12 @@ __export(MobilePopup_exports, {
|
|
|
41
41
|
});
|
|
42
42
|
module.exports = __toCommonJS(MobilePopup_exports);
|
|
43
43
|
var import_antd = require("antd");
|
|
44
|
-
var import_antd_mobile = require("antd-mobile");
|
|
45
44
|
var import_react = __toESM(require("react"));
|
|
46
|
-
var import_antd_mobile_icons = require("antd-mobile-icons");
|
|
47
45
|
var import_MobilePopup = require("./MobilePopup.style");
|
|
48
46
|
var import_react_i18next = require("react-i18next");
|
|
47
|
+
var import_lazy_helper = require("../lazy-helper");
|
|
48
|
+
const { Popup } = (0, import_lazy_helper.lazy)(() => import("antd-mobile"), "Popup");
|
|
49
|
+
const { CloseOutline } = (0, import_lazy_helper.lazy)(() => import("antd-mobile-icons"), "CloseOutline");
|
|
49
50
|
const MobilePopup = /* @__PURE__ */ __name((props) => {
|
|
50
51
|
const { title, visible, onClose: closePopup, children, minHeight, className, footer } = props;
|
|
51
52
|
const { t } = (0, import_react_i18next.useTranslation)();
|
|
@@ -67,7 +68,7 @@ const MobilePopup = /* @__PURE__ */ __name((props) => {
|
|
|
67
68
|
};
|
|
68
69
|
}, []);
|
|
69
70
|
return /* @__PURE__ */ import_react.default.createElement(import_antd.ConfigProvider, { theme }, /* @__PURE__ */ import_react.default.createElement(
|
|
70
|
-
|
|
71
|
+
Popup,
|
|
71
72
|
{
|
|
72
73
|
className: `${componentCls} ${hashId} ${className || ""}`,
|
|
73
74
|
visible,
|
|
@@ -81,7 +82,7 @@ const MobilePopup = /* @__PURE__ */ __name((props) => {
|
|
|
81
82
|
style,
|
|
82
83
|
destroyOnClose: true
|
|
83
84
|
},
|
|
84
|
-
/* @__PURE__ */ import_react.default.createElement("div", { className: "nb-mobile-action-drawer-header" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "nb-mobile-action-drawer-placeholder" }, /* @__PURE__ */ import_react.default.createElement(
|
|
85
|
+
/* @__PURE__ */ import_react.default.createElement("div", { className: "nb-mobile-action-drawer-header" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "nb-mobile-action-drawer-placeholder" }, /* @__PURE__ */ import_react.default.createElement(CloseOutline, null)), /* @__PURE__ */ import_react.default.createElement("span", null, title), /* @__PURE__ */ import_react.default.createElement(
|
|
85
86
|
"span",
|
|
86
87
|
{
|
|
87
88
|
className: "nb-mobile-action-drawer-close-icon",
|
|
@@ -90,7 +91,7 @@ const MobilePopup = /* @__PURE__ */ __name((props) => {
|
|
|
90
91
|
tabIndex: 0,
|
|
91
92
|
"aria-label": t("Close")
|
|
92
93
|
},
|
|
93
|
-
/* @__PURE__ */ import_react.default.createElement(
|
|
94
|
+
/* @__PURE__ */ import_react.default.createElement(CloseOutline, null)
|
|
94
95
|
)),
|
|
95
96
|
children,
|
|
96
97
|
footer && /* @__PURE__ */ import_react.default.createElement("div", { className: "nb-mobile-action-drawer-footer" }, footer)
|
|
@@ -430,7 +430,7 @@ const AddSubModelButtonCore = /* @__PURE__ */ __name(function AddSubModelButton(
|
|
|
430
430
|
}
|
|
431
431
|
let addedModel;
|
|
432
432
|
try {
|
|
433
|
-
addedModel = model.flowEngine.
|
|
433
|
+
addedModel = await model.flowEngine.createModelAsync({
|
|
434
434
|
...import_lodash.default.cloneDeep(createOpts),
|
|
435
435
|
parentId: model.uid,
|
|
436
436
|
subKey: subModelKey,
|
|
@@ -44,7 +44,7 @@ __export(utils_exports, {
|
|
|
44
44
|
buildWrapperFieldChildren: () => buildWrapperFieldChildren
|
|
45
45
|
});
|
|
46
46
|
module.exports = __toCommonJS(utils_exports);
|
|
47
|
-
var
|
|
47
|
+
var import_lodash = __toESM(require("lodash"));
|
|
48
48
|
var import_utils = require("../../utils");
|
|
49
49
|
async function callHideFunction(hide, ctx) {
|
|
50
50
|
if (typeof hide === "function") {
|
|
@@ -107,7 +107,7 @@ function buildSubModelChildren(M, ctx) {
|
|
|
107
107
|
const extraArg = args && args.length > 0 ? args[args.length - 1] : void 0;
|
|
108
108
|
const defaultOpts = await (0, import_utils.resolveCreateModelOptions)(meta == null ? void 0 : meta.createModelOptions, ctx, extraArg);
|
|
109
109
|
const childOpts = await (0, import_utils.resolveCreateModelOptions)(src, ctx, extraArg);
|
|
110
|
-
return
|
|
110
|
+
return import_lodash.default.merge({}, import_lodash.default.cloneDeep(defaultOpts), childOpts);
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
return node;
|
package/lib/flowEngine.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { FlowResource } from './resources';
|
|
|
8
8
|
import { Emitter } from './emitter';
|
|
9
9
|
import ModelOperationScheduler from './scheduler/ModelOperationScheduler';
|
|
10
10
|
import type { ScheduleOptions, ScheduledCancel } from './scheduler/ModelOperationScheduler';
|
|
11
|
-
import type { ActionDefinition, ApplyFlowCacheEntry, CreateModelOptions, EventDefinition, FlowModelOptions, IFlowModelRepository, ModelConstructor, PersistOptions, ResourceType } from './types';
|
|
11
|
+
import type { ActionDefinition, ApplyFlowCacheEntry, CreateModelOptions, EnsureBatchResult, EventDefinition, FlowModelLoaderMap, FlowModelOptions, IFlowModelRepository, ModelConstructor, PersistOptions, ResourceType } from './types';
|
|
12
12
|
/**
|
|
13
13
|
* FlowEngine is the core class of the flow engine, responsible for managing flow models, actions, model repository, and more.
|
|
14
14
|
* It provides capabilities for registering, creating, finding, persisting, replacing, and moving models.
|
|
@@ -47,6 +47,28 @@ export declare class FlowEngine {
|
|
|
47
47
|
* @private
|
|
48
48
|
*/
|
|
49
49
|
private _modelClasses;
|
|
50
|
+
/**
|
|
51
|
+
* Registered model entries.
|
|
52
|
+
* Key is the model class name, value is the model loader entry.
|
|
53
|
+
* @private
|
|
54
|
+
*/
|
|
55
|
+
private _modelLoaders;
|
|
56
|
+
/**
|
|
57
|
+
* In-flight model loading promises.
|
|
58
|
+
* Key is the model class name, value is the loading promise.
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
private _loadingModelPromises;
|
|
62
|
+
/**
|
|
63
|
+
* Whether model-loader preload has completed in this session.
|
|
64
|
+
* @private
|
|
65
|
+
*/
|
|
66
|
+
private _modelLoadersPreloaded;
|
|
67
|
+
/**
|
|
68
|
+
* In-flight model-loader preload promise.
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
private _modelLoadersPreloadPromise?;
|
|
50
72
|
/**
|
|
51
73
|
* Created model instances.
|
|
52
74
|
* Key is the model instance UID, value is the model instance object.
|
|
@@ -226,6 +248,10 @@ export declare class FlowEngine {
|
|
|
226
248
|
* Get all registered global events.
|
|
227
249
|
*/
|
|
228
250
|
getEvents<TModel extends FlowModel = FlowModel>(): Map<string, EventDefinition<TModel>>;
|
|
251
|
+
/**
|
|
252
|
+
* for proxy instance, the #registerModel can't be called.
|
|
253
|
+
*/
|
|
254
|
+
private _registerModel;
|
|
229
255
|
/**
|
|
230
256
|
* Register multiple model classes.
|
|
231
257
|
* @param {Record<string, ModelConstructor>} models Model class map, key is model name, value is model constructor
|
|
@@ -234,6 +260,99 @@ export declare class FlowEngine {
|
|
|
234
260
|
* flowEngine.registerModels({ UserModel, OrderModel });
|
|
235
261
|
*/
|
|
236
262
|
registerModels(models: Record<string, ModelConstructor | typeof FlowModel<any>>): void;
|
|
263
|
+
/**
|
|
264
|
+
* Register multiple model loader entries.
|
|
265
|
+
* @param {FlowModelLoaderMap} loaders Model loader entry map, key is model name, value is the model loader entry
|
|
266
|
+
* @returns {void}
|
|
267
|
+
* @example
|
|
268
|
+
* flowEngine.registerModelLoaders({
|
|
269
|
+
* DemoModel: {
|
|
270
|
+
* loader: () => import('./models/DemoModel'),
|
|
271
|
+
* },
|
|
272
|
+
* });
|
|
273
|
+
*/
|
|
274
|
+
registerModelLoaders(loaders: FlowModelLoaderMap): void;
|
|
275
|
+
/**
|
|
276
|
+
* Get a registered model class (constructor) asynchronously.
|
|
277
|
+
* This will first ensure the model loader entry is resolved.
|
|
278
|
+
* @param {string} name Model class name
|
|
279
|
+
* @returns {Promise<ModelConstructor | undefined>} Model constructor, or undefined if not found
|
|
280
|
+
*/
|
|
281
|
+
getModelClassAsync(name: string): Promise<ModelConstructor | undefined>;
|
|
282
|
+
/**
|
|
283
|
+
* Get all registered model classes asynchronously.
|
|
284
|
+
* This will first ensure all registered model loader entries are resolved.
|
|
285
|
+
* @returns {Promise<Map<string, ModelConstructor>>} Model class map
|
|
286
|
+
*/
|
|
287
|
+
getModelClassesAsync(): Promise<Map<string, ModelConstructor>>;
|
|
288
|
+
/**
|
|
289
|
+
* Create and register a model instance asynchronously.
|
|
290
|
+
* This will first ensure all string-based model references in the model tree are resolved.
|
|
291
|
+
* @template T FlowModel subclass type, defaults to FlowModel.
|
|
292
|
+
* @param {CreateModelOptions} options Model creation options
|
|
293
|
+
* @returns {Promise<T>} Created model instance
|
|
294
|
+
*/
|
|
295
|
+
createModelAsync<T extends FlowModel = FlowModel>(options: CreateModelOptions, extra?: {
|
|
296
|
+
delegateToParent?: boolean;
|
|
297
|
+
delegate?: FlowContext;
|
|
298
|
+
}): Promise<T>;
|
|
299
|
+
/**
|
|
300
|
+
* Normalize a loader result into a model constructor.
|
|
301
|
+
* @param {string} name Model class name
|
|
302
|
+
* @param {FlowModelLoaderResult} loaded Loader result
|
|
303
|
+
* @returns {ModelConstructor | null} Normalized model constructor
|
|
304
|
+
* @private
|
|
305
|
+
*/
|
|
306
|
+
private normalizeModelLoaderResult;
|
|
307
|
+
/**
|
|
308
|
+
* Collect string-based model names from a model tree.
|
|
309
|
+
* @param {unknown} data Model tree data
|
|
310
|
+
* @param {Set<string>} names Model name set
|
|
311
|
+
* @private
|
|
312
|
+
*/
|
|
313
|
+
private collectModelNamesFromTree;
|
|
314
|
+
/**
|
|
315
|
+
* Collect additional model names from object-form meta.createModelOptions defaults.
|
|
316
|
+
* @param {ModelConstructor} modelClass Model class constructor
|
|
317
|
+
* @param {Set<string>} names Model name set
|
|
318
|
+
* @private
|
|
319
|
+
*/
|
|
320
|
+
private collectModelNamesFromMetaDefaults;
|
|
321
|
+
/**
|
|
322
|
+
* Ensure a single model class is available.
|
|
323
|
+
* @param {string} name Model class name
|
|
324
|
+
* @returns {Promise<ModelConstructor | null>} Model constructor or null when resolution fails
|
|
325
|
+
* @private
|
|
326
|
+
*/
|
|
327
|
+
private ensureModel;
|
|
328
|
+
/**
|
|
329
|
+
* Ensure multiple model classes are available.
|
|
330
|
+
* @param {string[]} names Model class names
|
|
331
|
+
* @returns {Promise<EnsureBatchResult>} Batch ensure result
|
|
332
|
+
* @private
|
|
333
|
+
*/
|
|
334
|
+
private ensureModels;
|
|
335
|
+
/**
|
|
336
|
+
* Resolve all unresolved string-based model references in a model tree before synchronous creation begins.
|
|
337
|
+
*
|
|
338
|
+
* Use this when you already have a model tree object, such as repository-returned data or resolved
|
|
339
|
+
* `createModelOptions`, and you need to ensure every string `use` in that tree has been loaded and
|
|
340
|
+
* registered into `_modelClasses` before calling `createModel()`.
|
|
341
|
+
*
|
|
342
|
+
* @param {unknown} data Model tree data
|
|
343
|
+
* @returns {Promise<EnsureBatchResult>} Batch ensure result
|
|
344
|
+
*/
|
|
345
|
+
resolveModelTree(data: unknown): Promise<EnsureBatchResult>;
|
|
346
|
+
/**
|
|
347
|
+
* Preload all currently registered unresolved model loaders.
|
|
348
|
+
*
|
|
349
|
+
* This method is intended for flow-settings/discovery style entry points that need registered model
|
|
350
|
+
* classes to exist before UI is rendered, without requiring callers to know which specific models
|
|
351
|
+
* will be touched next.
|
|
352
|
+
*
|
|
353
|
+
* @returns {Promise<EnsureBatchResult>} Batch ensure result
|
|
354
|
+
*/
|
|
355
|
+
preloadModelLoaders(): Promise<EnsureBatchResult>;
|
|
237
356
|
registerResources(resources: Record<string, any>): void;
|
|
238
357
|
createResource<T = FlowResource>(resourceType: ResourceType<T>, options?: {
|
|
239
358
|
context?: FlowContext;
|
package/lib/flowEngine.js
CHANGED
|
@@ -83,6 +83,28 @@ const _FlowEngine = class _FlowEngine {
|
|
|
83
83
|
* @private
|
|
84
84
|
*/
|
|
85
85
|
__publicField(this, "_modelClasses", import_reactive.observable.shallow(/* @__PURE__ */ new Map()));
|
|
86
|
+
/**
|
|
87
|
+
* Registered model entries.
|
|
88
|
+
* Key is the model class name, value is the model loader entry.
|
|
89
|
+
* @private
|
|
90
|
+
*/
|
|
91
|
+
__publicField(this, "_modelLoaders", /* @__PURE__ */ new Map());
|
|
92
|
+
/**
|
|
93
|
+
* In-flight model loading promises.
|
|
94
|
+
* Key is the model class name, value is the loading promise.
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
__publicField(this, "_loadingModelPromises", /* @__PURE__ */ new Map());
|
|
98
|
+
/**
|
|
99
|
+
* Whether model-loader preload has completed in this session.
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
__publicField(this, "_modelLoadersPreloaded", false);
|
|
103
|
+
/**
|
|
104
|
+
* In-flight model-loader preload promise.
|
|
105
|
+
* @private
|
|
106
|
+
*/
|
|
107
|
+
__publicField(this, "_modelLoadersPreloadPromise");
|
|
86
108
|
/**
|
|
87
109
|
* Created model instances.
|
|
88
110
|
* Key is the model instance UID, value is the model instance object.
|
|
@@ -372,6 +394,16 @@ const _FlowEngine = class _FlowEngine {
|
|
|
372
394
|
getEvents() {
|
|
373
395
|
return this._eventRegistry.getEvents();
|
|
374
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* for proxy instance, the #registerModel can't be called.
|
|
399
|
+
*/
|
|
400
|
+
_registerModel(name, modelClass) {
|
|
401
|
+
if (this._modelClasses.has(name)) {
|
|
402
|
+
console.warn(`FlowEngine: Model class with name '${name}' is already registered and will be overwritten.`);
|
|
403
|
+
}
|
|
404
|
+
Object.defineProperty(modelClass, "name", { value: name });
|
|
405
|
+
this._modelClasses.set(name, modelClass);
|
|
406
|
+
}
|
|
375
407
|
/**
|
|
376
408
|
* Register multiple model classes.
|
|
377
409
|
* @param {Record<string, ModelConstructor>} models Model class map, key is model name, value is model constructor
|
|
@@ -384,6 +416,264 @@ const _FlowEngine = class _FlowEngine {
|
|
|
384
416
|
__privateMethod(this, _FlowEngine_instances, registerModel_fn).call(this, name, modelClass);
|
|
385
417
|
}
|
|
386
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* Register multiple model loader entries.
|
|
421
|
+
* @param {FlowModelLoaderMap} loaders Model loader entry map, key is model name, value is the model loader entry
|
|
422
|
+
* @returns {void}
|
|
423
|
+
* @example
|
|
424
|
+
* flowEngine.registerModelLoaders({
|
|
425
|
+
* DemoModel: {
|
|
426
|
+
* loader: () => import('./models/DemoModel'),
|
|
427
|
+
* },
|
|
428
|
+
* });
|
|
429
|
+
*/
|
|
430
|
+
registerModelLoaders(loaders) {
|
|
431
|
+
let changed = false;
|
|
432
|
+
for (const [name, entry] of Object.entries(loaders)) {
|
|
433
|
+
if (this._modelLoaders.has(name)) {
|
|
434
|
+
console.warn(`FlowEngine: Model loader with name '${name}' is already registered and will be overwritten.`);
|
|
435
|
+
}
|
|
436
|
+
this._modelLoaders.set(name, entry);
|
|
437
|
+
changed = true;
|
|
438
|
+
}
|
|
439
|
+
if (changed) {
|
|
440
|
+
this._modelLoadersPreloaded = false;
|
|
441
|
+
this._modelLoadersPreloadPromise = void 0;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Get a registered model class (constructor) asynchronously.
|
|
446
|
+
* This will first ensure the model loader entry is resolved.
|
|
447
|
+
* @param {string} name Model class name
|
|
448
|
+
* @returns {Promise<ModelConstructor | undefined>} Model constructor, or undefined if not found
|
|
449
|
+
*/
|
|
450
|
+
async getModelClassAsync(name) {
|
|
451
|
+
await this.ensureModel(name);
|
|
452
|
+
return this.getModelClass(name);
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Get all registered model classes asynchronously.
|
|
456
|
+
* This will first ensure all registered model loader entries are resolved.
|
|
457
|
+
* @returns {Promise<Map<string, ModelConstructor>>} Model class map
|
|
458
|
+
*/
|
|
459
|
+
async getModelClassesAsync() {
|
|
460
|
+
await this.ensureModels(Array.from(this._modelLoaders.keys()));
|
|
461
|
+
return this.getModelClasses();
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Create and register a model instance asynchronously.
|
|
465
|
+
* This will first ensure all string-based model references in the model tree are resolved.
|
|
466
|
+
* @template T FlowModel subclass type, defaults to FlowModel.
|
|
467
|
+
* @param {CreateModelOptions} options Model creation options
|
|
468
|
+
* @returns {Promise<T>} Created model instance
|
|
469
|
+
*/
|
|
470
|
+
async createModelAsync(options, extra) {
|
|
471
|
+
await this.resolveModelTree(options);
|
|
472
|
+
return this.createModel(options, extra);
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Normalize a loader result into a model constructor.
|
|
476
|
+
* @param {string} name Model class name
|
|
477
|
+
* @param {FlowModelLoaderResult} loaded Loader result
|
|
478
|
+
* @returns {ModelConstructor | null} Normalized model constructor
|
|
479
|
+
* @private
|
|
480
|
+
*/
|
|
481
|
+
normalizeModelLoaderResult(name, loaded) {
|
|
482
|
+
if (typeof loaded === "function") {
|
|
483
|
+
return loaded;
|
|
484
|
+
}
|
|
485
|
+
if (loaded && typeof loaded === "object") {
|
|
486
|
+
const defaultExport = loaded.default;
|
|
487
|
+
if (typeof defaultExport === "function") {
|
|
488
|
+
return defaultExport;
|
|
489
|
+
}
|
|
490
|
+
const namedExport = loaded[name];
|
|
491
|
+
if (typeof namedExport === "function") {
|
|
492
|
+
return namedExport;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
console.warn(`FlowEngine: model loader for '${name}' did not resolve to a valid model constructor.`);
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Collect string-based model names from a model tree.
|
|
500
|
+
* @param {unknown} data Model tree data
|
|
501
|
+
* @param {Set<string>} names Model name set
|
|
502
|
+
* @private
|
|
503
|
+
*/
|
|
504
|
+
collectModelNamesFromTree(data, names) {
|
|
505
|
+
if (!data || typeof data !== "object") {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (Array.isArray(data)) {
|
|
509
|
+
data.forEach((item) => this.collectModelNamesFromTree(item, names));
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const tree = data;
|
|
513
|
+
if (typeof tree.use === "string") {
|
|
514
|
+
names.add(tree.use);
|
|
515
|
+
}
|
|
516
|
+
const subModels = tree.subModels;
|
|
517
|
+
if (!subModels || typeof subModels !== "object") {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
Object.values(subModels).forEach((value) => {
|
|
521
|
+
this.collectModelNamesFromTree(value, names);
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Collect additional model names from object-form meta.createModelOptions defaults.
|
|
526
|
+
* @param {ModelConstructor} modelClass Model class constructor
|
|
527
|
+
* @param {Set<string>} names Model name set
|
|
528
|
+
* @private
|
|
529
|
+
*/
|
|
530
|
+
collectModelNamesFromMetaDefaults(modelClass, names) {
|
|
531
|
+
var _a;
|
|
532
|
+
const metaCreate = (_a = modelClass.meta) == null ? void 0 : _a.createModelOptions;
|
|
533
|
+
if (metaCreate && typeof metaCreate === "object") {
|
|
534
|
+
this.collectModelNamesFromTree(metaCreate, names);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Ensure a single model class is available.
|
|
539
|
+
* @param {string} name Model class name
|
|
540
|
+
* @returns {Promise<ModelConstructor | null>} Model constructor or null when resolution fails
|
|
541
|
+
* @private
|
|
542
|
+
*/
|
|
543
|
+
async ensureModel(name) {
|
|
544
|
+
const existing = this._modelClasses.get(name);
|
|
545
|
+
if (existing) {
|
|
546
|
+
return existing;
|
|
547
|
+
}
|
|
548
|
+
const inflight = this._loadingModelPromises.get(name);
|
|
549
|
+
if (inflight) {
|
|
550
|
+
return inflight;
|
|
551
|
+
}
|
|
552
|
+
const entry = this._modelLoaders.get(name);
|
|
553
|
+
if (!entry) {
|
|
554
|
+
console.warn(`FlowEngine: Model entry '${name}' not found. Falling back to ErrorFlowModel when needed.`);
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
const promise = (async () => {
|
|
558
|
+
try {
|
|
559
|
+
const loaded = await entry.loader();
|
|
560
|
+
const modelClass = this.normalizeModelLoaderResult(name, loaded);
|
|
561
|
+
if (!modelClass) {
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
this._registerModel(name, modelClass);
|
|
565
|
+
return modelClass;
|
|
566
|
+
} catch (error) {
|
|
567
|
+
console.warn(`FlowEngine: Failed to load model '${name}'. Falling back to ErrorFlowModel when needed.`, error);
|
|
568
|
+
return null;
|
|
569
|
+
} finally {
|
|
570
|
+
this._loadingModelPromises.delete(name);
|
|
571
|
+
}
|
|
572
|
+
})();
|
|
573
|
+
this._loadingModelPromises.set(name, promise);
|
|
574
|
+
return promise;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Ensure multiple model classes are available.
|
|
578
|
+
* @param {string[]} names Model class names
|
|
579
|
+
* @returns {Promise<EnsureBatchResult>} Batch ensure result
|
|
580
|
+
* @private
|
|
581
|
+
*/
|
|
582
|
+
async ensureModels(names) {
|
|
583
|
+
const requested = Array.from(new Set(names.filter((name) => !!name)));
|
|
584
|
+
const loaded = [];
|
|
585
|
+
const failed = [];
|
|
586
|
+
const results = await Promise.all(
|
|
587
|
+
requested.map(async (name) => {
|
|
588
|
+
const modelClass = await this.ensureModel(name);
|
|
589
|
+
return { name, modelClass };
|
|
590
|
+
})
|
|
591
|
+
);
|
|
592
|
+
results.forEach(({ name, modelClass }) => {
|
|
593
|
+
if (modelClass) {
|
|
594
|
+
loaded.push(name);
|
|
595
|
+
} else {
|
|
596
|
+
failed.push({ name });
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
return { requested, loaded, failed };
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Resolve all unresolved string-based model references in a model tree before synchronous creation begins.
|
|
603
|
+
*
|
|
604
|
+
* Use this when you already have a model tree object, such as repository-returned data or resolved
|
|
605
|
+
* `createModelOptions`, and you need to ensure every string `use` in that tree has been loaded and
|
|
606
|
+
* registered into `_modelClasses` before calling `createModel()`.
|
|
607
|
+
*
|
|
608
|
+
* @param {unknown} data Model tree data
|
|
609
|
+
* @returns {Promise<EnsureBatchResult>} Batch ensure result
|
|
610
|
+
*/
|
|
611
|
+
async resolveModelTree(data) {
|
|
612
|
+
const requested = /* @__PURE__ */ new Set();
|
|
613
|
+
const loaded = /* @__PURE__ */ new Set();
|
|
614
|
+
const failed = /* @__PURE__ */ new Map();
|
|
615
|
+
const processed = /* @__PURE__ */ new Set();
|
|
616
|
+
const pending = /* @__PURE__ */ new Set();
|
|
617
|
+
this.collectModelNamesFromTree(data, pending);
|
|
618
|
+
while (pending.size > 0) {
|
|
619
|
+
const batch = Array.from(pending).filter((name) => !processed.has(name));
|
|
620
|
+
pending.clear();
|
|
621
|
+
if (batch.length === 0) {
|
|
622
|
+
break;
|
|
623
|
+
}
|
|
624
|
+
batch.forEach((name) => requested.add(name));
|
|
625
|
+
const result = await this.ensureModels(batch);
|
|
626
|
+
result.loaded.forEach((name) => {
|
|
627
|
+
processed.add(name);
|
|
628
|
+
loaded.add(name);
|
|
629
|
+
const modelClass = this.getModelClass(name);
|
|
630
|
+
if (modelClass) {
|
|
631
|
+
const discovered = /* @__PURE__ */ new Set();
|
|
632
|
+
this.collectModelNamesFromMetaDefaults(modelClass, discovered);
|
|
633
|
+
discovered.forEach((discoveredName) => {
|
|
634
|
+
if (!processed.has(discoveredName)) {
|
|
635
|
+
pending.add(discoveredName);
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
result.failed.forEach((item) => {
|
|
641
|
+
processed.add(item.name);
|
|
642
|
+
failed.set(item.name, item);
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
return {
|
|
646
|
+
requested: Array.from(requested),
|
|
647
|
+
loaded: Array.from(loaded),
|
|
648
|
+
failed: Array.from(failed.values())
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Preload all currently registered unresolved model loaders.
|
|
653
|
+
*
|
|
654
|
+
* This method is intended for flow-settings/discovery style entry points that need registered model
|
|
655
|
+
* classes to exist before UI is rendered, without requiring callers to know which specific models
|
|
656
|
+
* will be touched next.
|
|
657
|
+
*
|
|
658
|
+
* @returns {Promise<EnsureBatchResult>} Batch ensure result
|
|
659
|
+
*/
|
|
660
|
+
async preloadModelLoaders() {
|
|
661
|
+
const unresolved = Array.from(this._modelLoaders.keys()).filter((name) => !this._modelClasses.has(name));
|
|
662
|
+
if (unresolved.length === 0) {
|
|
663
|
+
this._modelLoadersPreloaded = true;
|
|
664
|
+
return { requested: [], loaded: [], failed: [] };
|
|
665
|
+
}
|
|
666
|
+
if (this._modelLoadersPreloadPromise) {
|
|
667
|
+
return this._modelLoadersPreloadPromise;
|
|
668
|
+
}
|
|
669
|
+
this._modelLoadersPreloadPromise = (async () => {
|
|
670
|
+
const result = await this.ensureModels(unresolved);
|
|
671
|
+
this._modelLoadersPreloaded = result.failed.length === 0;
|
|
672
|
+
this._modelLoadersPreloadPromise = void 0;
|
|
673
|
+
return result;
|
|
674
|
+
})();
|
|
675
|
+
return this._modelLoadersPreloadPromise;
|
|
676
|
+
}
|
|
387
677
|
registerResources(resources) {
|
|
388
678
|
for (const [name, resourceClass] of Object.entries(resources)) {
|
|
389
679
|
this._resources.set(name, resourceClass);
|
|
@@ -732,7 +1022,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
732
1022
|
* Hydrate a model into current engine from an already-existing model instance in previous engines.
|
|
733
1023
|
* - Avoids repository requests when the model tree is already present in memory.
|
|
734
1024
|
*/
|
|
735
|
-
hydrateModelFromPreviousEngines(options, extra) {
|
|
1025
|
+
async hydrateModelFromPreviousEngines(options, extra) {
|
|
736
1026
|
var _a;
|
|
737
1027
|
const uid = options == null ? void 0 : options.uid;
|
|
738
1028
|
const parentId = options == null ? void 0 : options.parentId;
|
|
@@ -744,7 +1034,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
744
1034
|
}
|
|
745
1035
|
if (existing) {
|
|
746
1036
|
const data = existing.serialize();
|
|
747
|
-
return this.
|
|
1037
|
+
return this.createModelAsync(data, extra);
|
|
748
1038
|
}
|
|
749
1039
|
}
|
|
750
1040
|
if (parentId && subKey) {
|
|
@@ -755,10 +1045,10 @@ const _FlowEngine = class _FlowEngine {
|
|
|
755
1045
|
if (!localParent) {
|
|
756
1046
|
const parentData = parentFromPrev.serialize();
|
|
757
1047
|
delete parentData.subModels;
|
|
758
|
-
localParent = this.
|
|
1048
|
+
localParent = await this.createModelAsync(parentData, extra);
|
|
759
1049
|
}
|
|
760
1050
|
const modelData = modelFromPrev.serialize();
|
|
761
|
-
const localModel = this.
|
|
1051
|
+
const localModel = await this.createModelAsync(modelData, extra);
|
|
762
1052
|
const mounted = (_a = localParent.subModels) == null ? void 0 : _a[subKey];
|
|
763
1053
|
if (Array.isArray(mounted)) {
|
|
764
1054
|
const exists = mounted.some((m) => (m == null ? void 0 : m.uid) === (localModel == null ? void 0 : localModel.uid));
|
|
@@ -794,20 +1084,21 @@ const _FlowEngine = class _FlowEngine {
|
|
|
794
1084
|
if (model) {
|
|
795
1085
|
return model;
|
|
796
1086
|
}
|
|
797
|
-
const hydrated = this.hydrateModelFromPreviousEngines(options);
|
|
1087
|
+
const hydrated = await this.hydrateModelFromPreviousEngines(options);
|
|
798
1088
|
if (hydrated) {
|
|
799
1089
|
return hydrated;
|
|
800
1090
|
}
|
|
801
1091
|
}
|
|
802
1092
|
const data = await this._modelRepository.findOne(options);
|
|
803
1093
|
if (!(data == null ? void 0 : data.uid)) return null;
|
|
1094
|
+
await this.resolveModelTree(data);
|
|
804
1095
|
if (refresh) {
|
|
805
1096
|
const existing = this.getModel(data.uid);
|
|
806
1097
|
if (existing) {
|
|
807
1098
|
this.removeModelWithSubModels(existing.uid);
|
|
808
1099
|
}
|
|
809
1100
|
}
|
|
810
|
-
return this.
|
|
1101
|
+
return this.createModelAsync(data);
|
|
811
1102
|
}
|
|
812
1103
|
/**
|
|
813
1104
|
* Find a sub-model by parent model ID and subKey.
|
|
@@ -845,16 +1136,16 @@ const _FlowEngine = class _FlowEngine {
|
|
|
845
1136
|
if (m) {
|
|
846
1137
|
return m;
|
|
847
1138
|
}
|
|
848
|
-
const hydrated = this.hydrateModelFromPreviousEngines(options, extra);
|
|
1139
|
+
const hydrated = await this.hydrateModelFromPreviousEngines(options, extra);
|
|
849
1140
|
if (hydrated) {
|
|
850
1141
|
return hydrated;
|
|
851
1142
|
}
|
|
852
1143
|
const data = await this._modelRepository.findOne(options);
|
|
853
1144
|
let model = null;
|
|
854
1145
|
if (data == null ? void 0 : data.uid) {
|
|
855
|
-
model = this.
|
|
1146
|
+
model = await this.createModelAsync(data, extra);
|
|
856
1147
|
} else {
|
|
857
|
-
model = this.
|
|
1148
|
+
model = await this.createModelAsync(options, extra);
|
|
858
1149
|
if (!(extra == null ? void 0 : extra.skipSave)) {
|
|
859
1150
|
await model.save();
|
|
860
1151
|
}
|
|
@@ -1098,11 +1389,7 @@ _FlowEngine_instances = new WeakSet();
|
|
|
1098
1389
|
* @private
|
|
1099
1390
|
*/
|
|
1100
1391
|
registerModel_fn = /* @__PURE__ */ __name(function(name, modelClass) {
|
|
1101
|
-
|
|
1102
|
-
console.warn(`FlowEngine: Model class with name '${name}' is already registered and will be overwritten.`);
|
|
1103
|
-
}
|
|
1104
|
-
Object.defineProperty(modelClass, "name", { value: name });
|
|
1105
|
-
this._modelClasses.set(name, modelClass);
|
|
1392
|
+
return this._registerModel(name, modelClass);
|
|
1106
1393
|
}, "#registerModel");
|
|
1107
1394
|
__name(_FlowEngine, "FlowEngine");
|
|
1108
1395
|
let FlowEngine = _FlowEngine;
|
package/lib/flowSettings.d.ts
CHANGED
|
@@ -79,12 +79,19 @@ export interface FlowSettingsOpenOptions {
|
|
|
79
79
|
/** 配置保存成功后触发的回调 */
|
|
80
80
|
onSaved?: () => void | Promise<void>;
|
|
81
81
|
}
|
|
82
|
+
export type FlowSettingsComponent = React.ComponentType<any>;
|
|
83
|
+
export type FlowSettingsComponentModule = {
|
|
84
|
+
default?: FlowSettingsComponent;
|
|
85
|
+
} | Record<string, FlowSettingsComponent>;
|
|
86
|
+
export type FlowSettingsComponentLoader = () => Promise<FlowSettingsComponentModule | FlowSettingsComponent>;
|
|
87
|
+
export type FlowSettingsComponentLoaderMap = Record<string, FlowSettingsComponentLoader>;
|
|
82
88
|
export declare class FlowSettings {
|
|
83
89
|
#private;
|
|
84
90
|
components: Record<string, any>;
|
|
85
91
|
scopes: Record<string, any>;
|
|
86
92
|
private antdComponentsLoaded;
|
|
87
93
|
enabled: boolean;
|
|
94
|
+
private engine;
|
|
88
95
|
toolbarItems: ToolbarItemConfig[];
|
|
89
96
|
constructor(engine: FlowEngine);
|
|
90
97
|
on(event: 'beforeOpen', callback: (...args: any[]) => void): void;
|
|
@@ -110,6 +117,7 @@ export declare class FlowSettings {
|
|
|
110
117
|
* flowSettings.registerComponents({ MyComponent, AnotherComponent });
|
|
111
118
|
*/
|
|
112
119
|
registerComponents(components: Record<string, any>): void;
|
|
120
|
+
registerComponentLoaders(loaders: FlowSettingsComponentLoaderMap): void;
|
|
113
121
|
/**
|
|
114
122
|
* 添加作用域到 FlowSettings 的作用域注册表中。
|
|
115
123
|
* 这些作用域可以在 flow step 的 uiSchema 中使用。
|
|
@@ -122,17 +130,17 @@ export declare class FlowSettings {
|
|
|
122
130
|
/**
|
|
123
131
|
* 启用流程设置组件的显示
|
|
124
132
|
* @example
|
|
125
|
-
* flowSettings.enable();
|
|
133
|
+
* await flowSettings.enable();
|
|
126
134
|
*/
|
|
127
|
-
enable(): void
|
|
128
|
-
forceEnable(): void
|
|
135
|
+
enable(): Promise<void>;
|
|
136
|
+
forceEnable(): Promise<void>;
|
|
129
137
|
/**
|
|
130
138
|
* 禁用流程设置组件的显示
|
|
131
139
|
* @example
|
|
132
|
-
* flowSettings.disable();
|
|
140
|
+
* await flowSettings.disable();
|
|
133
141
|
*/
|
|
134
|
-
disable(): void
|
|
135
|
-
forceDisable(): void
|
|
142
|
+
disable(): Promise<void>;
|
|
143
|
+
forceDisable(): Promise<void>;
|
|
136
144
|
/**
|
|
137
145
|
* 添加扩展工具栏项目
|
|
138
146
|
* @param {ToolbarItemConfig} config 项目配置
|