@nocobase/flow-engine 2.1.0-beta.15 → 2.1.0-beta.17
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 +132 -1
- package/lib/flowEngine.js +360 -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 +46 -0
- package/lib/utils/runjsTemplateCompat.js +1 -1
- package/package.json +4 -4
- package/src/__tests__/flow-engine.test.ts +166 -0
- 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__/FlowModelRenderer.test.tsx +22 -0
- package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +3 -3
- package/src/components/settings/wrappers/contextual/__tests__/FlowsFloatContextMenu.test.tsx +6 -6
- 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 +412 -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 +59 -0
- package/src/utils/runjsTemplateCompat.ts +1 -1
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,274 @@ 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
|
+
* The `extends` field declares parent class(es) for async subclass discovery via `getSubclassesOfAsync`.
|
|
422
|
+
* It accepts `string | ModelConstructor | (string | ModelConstructor)[]` and is normalized to `string[]` internally.
|
|
423
|
+
* @param {FlowModelLoaderInputMap} loaders Model loader input map
|
|
424
|
+
* @returns {void}
|
|
425
|
+
* @example
|
|
426
|
+
* flowEngine.registerModelLoaders({
|
|
427
|
+
* DemoModel: {
|
|
428
|
+
* extends: 'BaseModel',
|
|
429
|
+
* loader: () => import('./models/DemoModel'),
|
|
430
|
+
* },
|
|
431
|
+
* });
|
|
432
|
+
*/
|
|
433
|
+
registerModelLoaders(loaders) {
|
|
434
|
+
let changed = false;
|
|
435
|
+
for (const [name, input] of Object.entries(loaders)) {
|
|
436
|
+
if (this._modelLoaders.has(name)) {
|
|
437
|
+
console.warn(`FlowEngine: Model loader with name '${name}' is already registered and will be overwritten.`);
|
|
438
|
+
}
|
|
439
|
+
const entry = {
|
|
440
|
+
loader: input.loader
|
|
441
|
+
};
|
|
442
|
+
if (input.extends != null) {
|
|
443
|
+
const raw = Array.isArray(input.extends) ? input.extends : [input.extends];
|
|
444
|
+
entry.extends = raw.map((item) => typeof item === "string" ? item : item.name);
|
|
445
|
+
}
|
|
446
|
+
this._modelLoaders.set(name, entry);
|
|
447
|
+
changed = true;
|
|
448
|
+
}
|
|
449
|
+
if (changed) {
|
|
450
|
+
this._modelLoadersPreloaded = false;
|
|
451
|
+
this._modelLoadersPreloadPromise = void 0;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Get a registered model class (constructor) asynchronously.
|
|
456
|
+
* This will first ensure the model loader entry is resolved.
|
|
457
|
+
* @param {string} name Model class name
|
|
458
|
+
* @returns {Promise<ModelConstructor | undefined>} Model constructor, or undefined if not found
|
|
459
|
+
*/
|
|
460
|
+
async getModelClassAsync(name) {
|
|
461
|
+
await this.ensureModel(name);
|
|
462
|
+
return this.getModelClass(name);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Get all registered model classes asynchronously.
|
|
466
|
+
* This will first ensure all registered model loader entries are resolved.
|
|
467
|
+
* @returns {Promise<Map<string, ModelConstructor>>} Model class map
|
|
468
|
+
*/
|
|
469
|
+
async getModelClassesAsync() {
|
|
470
|
+
await this.ensureModels(Array.from(this._modelLoaders.keys()));
|
|
471
|
+
return this.getModelClasses();
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Create and register a model instance asynchronously.
|
|
475
|
+
* This will first ensure all string-based model references in the model tree are resolved.
|
|
476
|
+
* @template T FlowModel subclass type, defaults to FlowModel.
|
|
477
|
+
* @param {CreateModelOptions} options Model creation options
|
|
478
|
+
* @returns {Promise<T>} Created model instance
|
|
479
|
+
*/
|
|
480
|
+
async createModelAsync(options, extra) {
|
|
481
|
+
await this.resolveModelTree(options);
|
|
482
|
+
return this.createModel(options, extra);
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Normalize a loader result into a model constructor.
|
|
486
|
+
* @param {string} name Model class name
|
|
487
|
+
* @param {FlowModelLoaderResult} loaded Loader result
|
|
488
|
+
* @returns {ModelConstructor | null} Normalized model constructor
|
|
489
|
+
* @private
|
|
490
|
+
*/
|
|
491
|
+
normalizeModelLoaderResult(name, loaded) {
|
|
492
|
+
if (typeof loaded === "function") {
|
|
493
|
+
return loaded;
|
|
494
|
+
}
|
|
495
|
+
if (loaded && typeof loaded === "object") {
|
|
496
|
+
const defaultExport = loaded.default;
|
|
497
|
+
if (typeof defaultExport === "function") {
|
|
498
|
+
return defaultExport;
|
|
499
|
+
}
|
|
500
|
+
const namedExport = loaded[name];
|
|
501
|
+
if (typeof namedExport === "function") {
|
|
502
|
+
return namedExport;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
console.warn(`FlowEngine: model loader for '${name}' did not resolve to a valid model constructor.`);
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Collect string-based model names from a model tree.
|
|
510
|
+
* @param {unknown} data Model tree data
|
|
511
|
+
* @param {Set<string>} names Model name set
|
|
512
|
+
* @private
|
|
513
|
+
*/
|
|
514
|
+
collectModelNamesFromTree(data, names) {
|
|
515
|
+
if (!data || typeof data !== "object") {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
if (Array.isArray(data)) {
|
|
519
|
+
data.forEach((item) => this.collectModelNamesFromTree(item, names));
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const tree = data;
|
|
523
|
+
if (typeof tree.use === "string") {
|
|
524
|
+
names.add(tree.use);
|
|
525
|
+
}
|
|
526
|
+
const subModels = tree.subModels;
|
|
527
|
+
if (!subModels || typeof subModels !== "object") {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
Object.values(subModels).forEach((value) => {
|
|
531
|
+
this.collectModelNamesFromTree(value, names);
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Collect additional model names from object-form meta.createModelOptions defaults.
|
|
536
|
+
* @param {ModelConstructor} modelClass Model class constructor
|
|
537
|
+
* @param {Set<string>} names Model name set
|
|
538
|
+
* @private
|
|
539
|
+
*/
|
|
540
|
+
collectModelNamesFromMetaDefaults(modelClass, names) {
|
|
541
|
+
var _a;
|
|
542
|
+
const metaCreate = (_a = modelClass.meta) == null ? void 0 : _a.createModelOptions;
|
|
543
|
+
if (metaCreate && typeof metaCreate === "object") {
|
|
544
|
+
this.collectModelNamesFromTree(metaCreate, names);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Ensure a single model class is available.
|
|
549
|
+
* @param {string} name Model class name
|
|
550
|
+
* @returns {Promise<ModelConstructor | null>} Model constructor or null when resolution fails
|
|
551
|
+
* @private
|
|
552
|
+
*/
|
|
553
|
+
async ensureModel(name) {
|
|
554
|
+
const existing = this._modelClasses.get(name);
|
|
555
|
+
if (existing) {
|
|
556
|
+
return existing;
|
|
557
|
+
}
|
|
558
|
+
const inflight = this._loadingModelPromises.get(name);
|
|
559
|
+
if (inflight) {
|
|
560
|
+
return inflight;
|
|
561
|
+
}
|
|
562
|
+
const entry = this._modelLoaders.get(name);
|
|
563
|
+
if (!entry) {
|
|
564
|
+
console.warn(`FlowEngine: Model entry '${name}' not found. Falling back to ErrorFlowModel when needed.`);
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
const promise = (async () => {
|
|
568
|
+
try {
|
|
569
|
+
const loaded = await entry.loader();
|
|
570
|
+
const modelClass = this.normalizeModelLoaderResult(name, loaded);
|
|
571
|
+
if (!modelClass) {
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
this._registerModel(name, modelClass);
|
|
575
|
+
return modelClass;
|
|
576
|
+
} catch (error) {
|
|
577
|
+
console.warn(`FlowEngine: Failed to load model '${name}'. Falling back to ErrorFlowModel when needed.`, error);
|
|
578
|
+
return null;
|
|
579
|
+
} finally {
|
|
580
|
+
this._loadingModelPromises.delete(name);
|
|
581
|
+
}
|
|
582
|
+
})();
|
|
583
|
+
this._loadingModelPromises.set(name, promise);
|
|
584
|
+
return promise;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Ensure multiple model classes are available.
|
|
588
|
+
* @param {string[]} names Model class names
|
|
589
|
+
* @returns {Promise<EnsureBatchResult>} Batch ensure result
|
|
590
|
+
* @private
|
|
591
|
+
*/
|
|
592
|
+
async ensureModels(names) {
|
|
593
|
+
const requested = Array.from(new Set(names.filter((name) => !!name)));
|
|
594
|
+
const loaded = [];
|
|
595
|
+
const failed = [];
|
|
596
|
+
const results = await Promise.all(
|
|
597
|
+
requested.map(async (name) => {
|
|
598
|
+
const modelClass = await this.ensureModel(name);
|
|
599
|
+
return { name, modelClass };
|
|
600
|
+
})
|
|
601
|
+
);
|
|
602
|
+
results.forEach(({ name, modelClass }) => {
|
|
603
|
+
if (modelClass) {
|
|
604
|
+
loaded.push(name);
|
|
605
|
+
} else {
|
|
606
|
+
failed.push({ name });
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
return { requested, loaded, failed };
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Resolve all unresolved string-based model references in a model tree before synchronous creation begins.
|
|
613
|
+
*
|
|
614
|
+
* Use this when you already have a model tree object, such as repository-returned data or resolved
|
|
615
|
+
* `createModelOptions`, and you need to ensure every string `use` in that tree has been loaded and
|
|
616
|
+
* registered into `_modelClasses` before calling `createModel()`.
|
|
617
|
+
*
|
|
618
|
+
* @param {unknown} data Model tree data
|
|
619
|
+
* @returns {Promise<EnsureBatchResult>} Batch ensure result
|
|
620
|
+
*/
|
|
621
|
+
async resolveModelTree(data) {
|
|
622
|
+
const requested = /* @__PURE__ */ new Set();
|
|
623
|
+
const loaded = /* @__PURE__ */ new Set();
|
|
624
|
+
const failed = /* @__PURE__ */ new Map();
|
|
625
|
+
const processed = /* @__PURE__ */ new Set();
|
|
626
|
+
const pending = /* @__PURE__ */ new Set();
|
|
627
|
+
this.collectModelNamesFromTree(data, pending);
|
|
628
|
+
while (pending.size > 0) {
|
|
629
|
+
const batch = Array.from(pending).filter((name) => !processed.has(name));
|
|
630
|
+
pending.clear();
|
|
631
|
+
if (batch.length === 0) {
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
batch.forEach((name) => requested.add(name));
|
|
635
|
+
const result = await this.ensureModels(batch);
|
|
636
|
+
result.loaded.forEach((name) => {
|
|
637
|
+
processed.add(name);
|
|
638
|
+
loaded.add(name);
|
|
639
|
+
const modelClass = this.getModelClass(name);
|
|
640
|
+
if (modelClass) {
|
|
641
|
+
const discovered = /* @__PURE__ */ new Set();
|
|
642
|
+
this.collectModelNamesFromMetaDefaults(modelClass, discovered);
|
|
643
|
+
discovered.forEach((discoveredName) => {
|
|
644
|
+
if (!processed.has(discoveredName)) {
|
|
645
|
+
pending.add(discoveredName);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
result.failed.forEach((item) => {
|
|
651
|
+
processed.add(item.name);
|
|
652
|
+
failed.set(item.name, item);
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
return {
|
|
656
|
+
requested: Array.from(requested),
|
|
657
|
+
loaded: Array.from(loaded),
|
|
658
|
+
failed: Array.from(failed.values())
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Preload all currently registered unresolved model loaders.
|
|
663
|
+
*
|
|
664
|
+
* This method is intended for flow-settings/discovery style entry points that need registered model
|
|
665
|
+
* classes to exist before UI is rendered, without requiring callers to know which specific models
|
|
666
|
+
* will be touched next.
|
|
667
|
+
*
|
|
668
|
+
* @returns {Promise<EnsureBatchResult>} Batch ensure result
|
|
669
|
+
*/
|
|
670
|
+
async preloadModelLoaders() {
|
|
671
|
+
const unresolved = Array.from(this._modelLoaders.keys()).filter((name) => !this._modelClasses.has(name));
|
|
672
|
+
if (unresolved.length === 0) {
|
|
673
|
+
this._modelLoadersPreloaded = true;
|
|
674
|
+
return { requested: [], loaded: [], failed: [] };
|
|
675
|
+
}
|
|
676
|
+
if (this._modelLoadersPreloadPromise) {
|
|
677
|
+
return this._modelLoadersPreloadPromise;
|
|
678
|
+
}
|
|
679
|
+
this._modelLoadersPreloadPromise = (async () => {
|
|
680
|
+
const result = await this.ensureModels(unresolved);
|
|
681
|
+
this._modelLoadersPreloaded = result.failed.length === 0;
|
|
682
|
+
this._modelLoadersPreloadPromise = void 0;
|
|
683
|
+
return result;
|
|
684
|
+
})();
|
|
685
|
+
return this._modelLoadersPreloadPromise;
|
|
686
|
+
}
|
|
387
687
|
registerResources(resources) {
|
|
388
688
|
for (const [name, resourceClass] of Object.entries(resources)) {
|
|
389
689
|
this._resources.set(name, resourceClass);
|
|
@@ -447,6 +747,55 @@ const _FlowEngine = class _FlowEngine {
|
|
|
447
747
|
}
|
|
448
748
|
return result;
|
|
449
749
|
}
|
|
750
|
+
/**
|
|
751
|
+
* Asynchronously get all subclasses of a base class, including those registered via model loaders.
|
|
752
|
+
* Merges results from already-loaded classes (_modelClasses) and async loader entries with matching `extends` declarations.
|
|
753
|
+
* Loader-resolved classes are validated with `isInheritedFrom`; mismatches are warned and excluded.
|
|
754
|
+
* @param {string | ModelConstructor} baseClass Base class name or constructor
|
|
755
|
+
* @param {(ModelClass: ModelConstructor, className: string) => boolean} [filter] Optional filter function
|
|
756
|
+
* @returns {Promise<Map<string, ModelConstructor>>} Model classes that are subclasses of the base class
|
|
757
|
+
*/
|
|
758
|
+
async getSubclassesOfAsync(baseClass, filter) {
|
|
759
|
+
var _a;
|
|
760
|
+
const baseClassName = typeof baseClass === "string" ? baseClass : baseClass.name;
|
|
761
|
+
let parentModelClass;
|
|
762
|
+
if (typeof baseClass === "string") {
|
|
763
|
+
if (!this.getModelClass(baseClass)) {
|
|
764
|
+
await this.ensureModel(baseClass);
|
|
765
|
+
}
|
|
766
|
+
parentModelClass = this.getModelClass(baseClass);
|
|
767
|
+
} else {
|
|
768
|
+
parentModelClass = baseClass;
|
|
769
|
+
}
|
|
770
|
+
if (!parentModelClass) {
|
|
771
|
+
return /* @__PURE__ */ new Map();
|
|
772
|
+
}
|
|
773
|
+
const result = this.getSubclassesOf(parentModelClass, filter);
|
|
774
|
+
const loaderCandidates = [];
|
|
775
|
+
for (const [name, entry] of this._modelLoaders) {
|
|
776
|
+
if (result.has(name) || this._modelClasses.has(name)) continue;
|
|
777
|
+
if ((_a = entry.extends) == null ? void 0 : _a.includes(baseClassName)) {
|
|
778
|
+
loaderCandidates.push(name);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (loaderCandidates.length > 0) {
|
|
782
|
+
await this.ensureModels(loaderCandidates);
|
|
783
|
+
}
|
|
784
|
+
for (const name of loaderCandidates) {
|
|
785
|
+
const ModelClass = this._modelClasses.get(name);
|
|
786
|
+
if (!ModelClass) continue;
|
|
787
|
+
if (!(0, import_utils.isInheritedFrom)(ModelClass, parentModelClass)) {
|
|
788
|
+
console.warn(
|
|
789
|
+
`FlowEngine: Model '${name}' declares extends '${baseClassName}' but does not actually inherit from it. Skipping.`
|
|
790
|
+
);
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
if (!filter || filter(ModelClass, name)) {
|
|
794
|
+
result.set(name, ModelClass);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return result;
|
|
798
|
+
}
|
|
450
799
|
/**
|
|
451
800
|
* Create and register a model instance.
|
|
452
801
|
* If an instance with the same UID exists, returns the existing instance.
|
|
@@ -732,7 +1081,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
732
1081
|
* Hydrate a model into current engine from an already-existing model instance in previous engines.
|
|
733
1082
|
* - Avoids repository requests when the model tree is already present in memory.
|
|
734
1083
|
*/
|
|
735
|
-
hydrateModelFromPreviousEngines(options, extra) {
|
|
1084
|
+
async hydrateModelFromPreviousEngines(options, extra) {
|
|
736
1085
|
var _a;
|
|
737
1086
|
const uid = options == null ? void 0 : options.uid;
|
|
738
1087
|
const parentId = options == null ? void 0 : options.parentId;
|
|
@@ -744,7 +1093,7 @@ const _FlowEngine = class _FlowEngine {
|
|
|
744
1093
|
}
|
|
745
1094
|
if (existing) {
|
|
746
1095
|
const data = existing.serialize();
|
|
747
|
-
return this.
|
|
1096
|
+
return this.createModelAsync(data, extra);
|
|
748
1097
|
}
|
|
749
1098
|
}
|
|
750
1099
|
if (parentId && subKey) {
|
|
@@ -755,10 +1104,10 @@ const _FlowEngine = class _FlowEngine {
|
|
|
755
1104
|
if (!localParent) {
|
|
756
1105
|
const parentData = parentFromPrev.serialize();
|
|
757
1106
|
delete parentData.subModels;
|
|
758
|
-
localParent = this.
|
|
1107
|
+
localParent = await this.createModelAsync(parentData, extra);
|
|
759
1108
|
}
|
|
760
1109
|
const modelData = modelFromPrev.serialize();
|
|
761
|
-
const localModel = this.
|
|
1110
|
+
const localModel = await this.createModelAsync(modelData, extra);
|
|
762
1111
|
const mounted = (_a = localParent.subModels) == null ? void 0 : _a[subKey];
|
|
763
1112
|
if (Array.isArray(mounted)) {
|
|
764
1113
|
const exists = mounted.some((m) => (m == null ? void 0 : m.uid) === (localModel == null ? void 0 : localModel.uid));
|
|
@@ -794,20 +1143,21 @@ const _FlowEngine = class _FlowEngine {
|
|
|
794
1143
|
if (model) {
|
|
795
1144
|
return model;
|
|
796
1145
|
}
|
|
797
|
-
const hydrated = this.hydrateModelFromPreviousEngines(options);
|
|
1146
|
+
const hydrated = await this.hydrateModelFromPreviousEngines(options);
|
|
798
1147
|
if (hydrated) {
|
|
799
1148
|
return hydrated;
|
|
800
1149
|
}
|
|
801
1150
|
}
|
|
802
1151
|
const data = await this._modelRepository.findOne(options);
|
|
803
1152
|
if (!(data == null ? void 0 : data.uid)) return null;
|
|
1153
|
+
await this.resolveModelTree(data);
|
|
804
1154
|
if (refresh) {
|
|
805
1155
|
const existing = this.getModel(data.uid);
|
|
806
1156
|
if (existing) {
|
|
807
1157
|
this.removeModelWithSubModels(existing.uid);
|
|
808
1158
|
}
|
|
809
1159
|
}
|
|
810
|
-
return this.
|
|
1160
|
+
return this.createModelAsync(data);
|
|
811
1161
|
}
|
|
812
1162
|
/**
|
|
813
1163
|
* Find a sub-model by parent model ID and subKey.
|
|
@@ -845,16 +1195,16 @@ const _FlowEngine = class _FlowEngine {
|
|
|
845
1195
|
if (m) {
|
|
846
1196
|
return m;
|
|
847
1197
|
}
|
|
848
|
-
const hydrated = this.hydrateModelFromPreviousEngines(options, extra);
|
|
1198
|
+
const hydrated = await this.hydrateModelFromPreviousEngines(options, extra);
|
|
849
1199
|
if (hydrated) {
|
|
850
1200
|
return hydrated;
|
|
851
1201
|
}
|
|
852
1202
|
const data = await this._modelRepository.findOne(options);
|
|
853
1203
|
let model = null;
|
|
854
1204
|
if (data == null ? void 0 : data.uid) {
|
|
855
|
-
model = this.
|
|
1205
|
+
model = await this.createModelAsync(data, extra);
|
|
856
1206
|
} else {
|
|
857
|
-
model = this.
|
|
1207
|
+
model = await this.createModelAsync(options, extra);
|
|
858
1208
|
if (!(extra == null ? void 0 : extra.skipSave)) {
|
|
859
1209
|
await model.save();
|
|
860
1210
|
}
|
|
@@ -1098,11 +1448,7 @@ _FlowEngine_instances = new WeakSet();
|
|
|
1098
1448
|
* @private
|
|
1099
1449
|
*/
|
|
1100
1450
|
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);
|
|
1451
|
+
return this._registerModel(name, modelClass);
|
|
1106
1452
|
}, "#registerModel");
|
|
1107
1453
|
__name(_FlowEngine, "FlowEngine");
|
|
1108
1454
|
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 项目配置
|
package/lib/flowSettings.js
CHANGED
|
@@ -65,6 +65,7 @@ var import_utils = require("./utils");
|
|
|
65
65
|
var import_exceptions = require("./utils/exceptions");
|
|
66
66
|
var import_useFlowStep = require("./hooks/useFlowStep");
|
|
67
67
|
var import_views = require("./views");
|
|
68
|
+
var import_lazy_helper = require("./lazy-helper");
|
|
68
69
|
var _forceEnabled, _emitter;
|
|
69
70
|
const Panel = import_antd.Collapse.Panel;
|
|
70
71
|
const _FlowSettings = class _FlowSettings {
|
|
@@ -73,10 +74,12 @@ const _FlowSettings = class _FlowSettings {
|
|
|
73
74
|
__publicField(this, "scopes", {});
|
|
74
75
|
__publicField(this, "antdComponentsLoaded", false);
|
|
75
76
|
__publicField(this, "enabled");
|
|
77
|
+
__publicField(this, "engine");
|
|
76
78
|
__privateAdd(this, _forceEnabled, false);
|
|
77
79
|
// 强制启用状态,主要用于设计模式下的强制启用
|
|
78
80
|
__publicField(this, "toolbarItems", []);
|
|
79
81
|
__privateAdd(this, _emitter, new import_emitter.Emitter());
|
|
82
|
+
this.engine = engine;
|
|
80
83
|
this.enabled = false;
|
|
81
84
|
engine.context.defineProperty("flowSettingsEnabled", {
|
|
82
85
|
get: /* @__PURE__ */ __name(() => this.enabled, "get"),
|
|
@@ -215,6 +218,29 @@ const _FlowSettings = class _FlowSettings {
|
|
|
215
218
|
this.components[name] = components[name];
|
|
216
219
|
});
|
|
217
220
|
}
|
|
221
|
+
registerComponentLoaders(loaders) {
|
|
222
|
+
Object.entries(loaders).forEach(([name, loader]) => {
|
|
223
|
+
if (this.components[name]) {
|
|
224
|
+
console.warn(`FlowSettings: Component with name '${name}' is already registered and will be overwritten.`);
|
|
225
|
+
}
|
|
226
|
+
this.components[name] = (0, import_lazy_helper.lazy)(async () => {
|
|
227
|
+
const loaded = await loader();
|
|
228
|
+
if (typeof loaded === "function") {
|
|
229
|
+
return { default: loaded };
|
|
230
|
+
}
|
|
231
|
+
if ((loaded == null ? void 0 : loaded.default) && typeof loaded.default === "function") {
|
|
232
|
+
return { default: loaded.default };
|
|
233
|
+
}
|
|
234
|
+
const namedComponent = loaded == null ? void 0 : loaded[name];
|
|
235
|
+
if (typeof namedComponent === "function") {
|
|
236
|
+
return { default: namedComponent };
|
|
237
|
+
}
|
|
238
|
+
throw new Error(
|
|
239
|
+
`FlowSettings: component loader for '${name}' must resolve to a React component or a module exporting it.`
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
}
|
|
218
244
|
/**
|
|
219
245
|
* 添加作用域到 FlowSettings 的作用域注册表中。
|
|
220
246
|
* 这些作用域可以在 flow step 的 uiSchema 中使用。
|
|
@@ -234,27 +260,29 @@ const _FlowSettings = class _FlowSettings {
|
|
|
234
260
|
/**
|
|
235
261
|
* 启用流程设置组件的显示
|
|
236
262
|
* @example
|
|
237
|
-
* flowSettings.enable();
|
|
263
|
+
* await flowSettings.enable();
|
|
238
264
|
*/
|
|
239
|
-
enable() {
|
|
265
|
+
async enable() {
|
|
266
|
+
await this.engine.preloadModelLoaders();
|
|
240
267
|
this.enabled = true;
|
|
241
268
|
}
|
|
242
|
-
forceEnable() {
|
|
269
|
+
async forceEnable() {
|
|
270
|
+
await this.engine.preloadModelLoaders();
|
|
243
271
|
__privateSet(this, _forceEnabled, true);
|
|
244
272
|
this.enabled = true;
|
|
245
273
|
}
|
|
246
274
|
/**
|
|
247
275
|
* 禁用流程设置组件的显示
|
|
248
276
|
* @example
|
|
249
|
-
* flowSettings.disable();
|
|
277
|
+
* await flowSettings.disable();
|
|
250
278
|
*/
|
|
251
|
-
disable() {
|
|
279
|
+
async disable() {
|
|
252
280
|
if (__privateGet(this, _forceEnabled)) {
|
|
253
281
|
return;
|
|
254
282
|
}
|
|
255
283
|
this.enabled = false;
|
|
256
284
|
}
|
|
257
|
-
forceDisable() {
|
|
285
|
+
async forceDisable() {
|
|
258
286
|
__privateSet(this, _forceEnabled, false);
|
|
259
287
|
this.enabled = false;
|
|
260
288
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
type LazyComponentType<M extends Record<string, any>, K extends keyof M> = {
|
|
10
|
+
[P in K]: M[P];
|
|
11
|
+
};
|
|
12
|
+
export declare function lazy<M extends Record<'default', any>>(factory: () => Promise<M>): M['default'];
|
|
13
|
+
export declare function lazy<M extends Record<string, any>, K extends keyof M = keyof M>(factory: () => Promise<M>, ...componentNames: K[]): LazyComponentType<M, K>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __create = Object.create;
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
13
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
15
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
17
|
+
var __export = (target, all) => {
|
|
18
|
+
for (var name in all)
|
|
19
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
30
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
31
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
32
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
33
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
34
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
35
|
+
mod
|
|
36
|
+
));
|
|
37
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
38
|
+
var lazy_helper_exports = {};
|
|
39
|
+
__export(lazy_helper_exports, {
|
|
40
|
+
lazy: () => lazy
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(lazy_helper_exports);
|
|
43
|
+
var import_react = __toESM(require("react"));
|
|
44
|
+
function lazy(factory, ...componentNames) {
|
|
45
|
+
if (componentNames.length === 0) {
|
|
46
|
+
const LazyComponent = (0, import_react.lazy)(
|
|
47
|
+
() => factory().then((module2) => ({
|
|
48
|
+
default: module2.default
|
|
49
|
+
}))
|
|
50
|
+
);
|
|
51
|
+
const Component = /* @__PURE__ */ __name((props) => /* @__PURE__ */ import_react.default.createElement(import_react.default.Suspense, { fallback: null }, /* @__PURE__ */ import_react.default.createElement(LazyComponent, { ...props })), "Component");
|
|
52
|
+
return Component;
|
|
53
|
+
}
|
|
54
|
+
return componentNames.reduce(
|
|
55
|
+
(acc, name) => {
|
|
56
|
+
const LazyComponent = (0, import_react.lazy)(
|
|
57
|
+
() => factory().then((module2) => ({
|
|
58
|
+
default: module2[name]
|
|
59
|
+
}))
|
|
60
|
+
);
|
|
61
|
+
acc[name] = (props) => /* @__PURE__ */ import_react.default.createElement(import_react.default.Suspense, { fallback: null }, /* @__PURE__ */ import_react.default.createElement(LazyComponent, { ...props }));
|
|
62
|
+
return acc;
|
|
63
|
+
},
|
|
64
|
+
{}
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
__name(lazy, "lazy");
|
|
68
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
69
|
+
0 && (module.exports = {
|
|
70
|
+
lazy
|
|
71
|
+
});
|