@nocobase/flow-engine
Version:
A standalone flow engine for NocoBase, managing workflows, models, and actions.
734 lines (732 loc) • 23.2 kB
JavaScript
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var data_source_exports = {};
__export(data_source_exports, {
Collection: () => Collection,
CollectionField: () => CollectionField,
CollectionManager: () => CollectionManager,
DataSource: () => DataSource,
DataSourceManager: () => DataSourceManager,
isFieldInterfaceMatch: () => isFieldInterfaceMatch,
jioToJoiSchema: () => import_jioToJoiSchema.jioToJoiSchema
});
module.exports = __toCommonJS(data_source_exports);
var import_reactive = require("@formily/reactive");
var import_lodash = __toESM(require("lodash"));
var import_jioToJoiSchema = require("./jioToJoiSchema");
const _DataSourceManager = class _DataSourceManager {
dataSources;
flowEngine;
constructor() {
this.dataSources = import_reactive.observable.shallow(/* @__PURE__ */ new Map());
}
setFlowEngine(flowEngine) {
this.flowEngine = flowEngine;
}
addDataSource(ds) {
if (this.dataSources.has(ds.key)) {
throw new Error(`DataSource with name ${ds.key} already exists`);
}
if (ds instanceof DataSource) {
this.dataSources.set(ds.key, ds);
} else {
const clz = ds.use || DataSource;
ds = new clz(ds);
this.dataSources.set(ds.key, ds);
}
ds.setDataSourceManager(this);
}
upsertDataSource(ds) {
var _a;
if (this.dataSources.has(ds.key)) {
(_a = this.dataSources.get(ds.key)) == null ? void 0 : _a.setOptions(ds);
} else {
this.addDataSource(ds);
}
}
removeDataSource(key) {
this.dataSources.delete(key);
}
clearDataSources() {
this.dataSources.clear();
}
getDataSources() {
return Array.from(this.dataSources.values());
}
getDataSource(key) {
return this.dataSources.get(key);
}
getCollection(dataSourceKey, collectionName) {
const ds = this.getDataSource(dataSourceKey);
if (!ds) return void 0;
return ds.collectionManager.getCollection(collectionName);
}
getCollectionField(fieldPathWithDataSource) {
const [dataSourceKey, ...otherKeys] = fieldPathWithDataSource.split(".");
const ds = this.getDataSource(dataSourceKey);
if (!ds) return void 0;
return ds.getCollectionField(otherKeys.join("."));
}
};
__name(_DataSourceManager, "DataSourceManager");
let DataSourceManager = _DataSourceManager;
const _DataSource = class _DataSource {
dataSourceManager;
collectionManager;
options;
constructor(options = {}) {
this.options = (0, import_reactive.observable)({ ...options });
this.collectionManager = new CollectionManager(this);
}
get flowEngine() {
return this.dataSourceManager.flowEngine;
}
get displayName() {
return this.options.displayName ? this.flowEngine.translate(this.options.displayName) : this.key;
}
get key() {
return this.options.key;
}
get name() {
return this.options.key;
}
setDataSourceManager(dataSourceManager) {
this.dataSourceManager = dataSourceManager;
}
getCollections() {
return this.collectionManager.getCollections();
}
getCollection(name) {
return this.collectionManager.getCollection(name);
}
/**
* @deprecated use getAssociation instead
*/
getAssocation(associationName) {
return this.getAssociation(associationName);
}
getAssociation(associationName) {
return this.collectionManager.getAssociation(associationName);
}
addCollection(collection) {
return this.collectionManager.addCollection(collection);
}
updateCollection(newOptions) {
return this.collectionManager.updateCollection(newOptions);
}
upsertCollection(options) {
return this.collectionManager.upsertCollection(options);
}
upsertCollections(collections) {
return this.collectionManager.upsertCollections(collections);
}
removeCollection(name) {
return this.collectionManager.removeCollection(name);
}
clearCollections() {
this.collectionManager.clearCollections();
}
setOptions(newOptions = {}) {
Object.keys(this.options).forEach((key) => delete this.options[key]);
Object.assign(this.options, newOptions);
}
getCollectionField(fieldPath) {
const [collectionName, ...otherKeys] = fieldPath.split(".");
const fieldName = otherKeys.join(".");
const collection = this.getCollection(collectionName);
if (!collection) {
throw new Error(`Collection ${collectionName} not found in data source ${this.key}`);
}
const field = collection.getFieldByPath(fieldName);
if (!field) {
return;
}
return field;
}
};
__name(_DataSource, "DataSource");
let DataSource = _DataSource;
const _CollectionManager = class _CollectionManager {
constructor(dataSource) {
this.dataSource = dataSource;
this.collections = import_reactive.observable.shallow(/* @__PURE__ */ new Map());
}
collections;
get flowEngine() {
return this.dataSource.flowEngine;
}
addCollection(collection) {
let col;
if (collection instanceof Collection) {
col = collection;
} else {
col = new Collection(collection);
}
col.setDataSource(this.dataSource);
col.initInherits();
this.collections.set(col.name, col);
}
removeCollection(name) {
this.collections.delete(name);
}
updateCollection(newOptions) {
const collection = this.getCollection(newOptions.name);
if (!collection) {
throw new Error(`Collection ${newOptions.name} not found`);
}
collection.setOptions(newOptions);
}
upsertCollection(options) {
if (this.collections.has(options.name)) {
this.updateCollection(options);
} else {
this.addCollection(options);
}
return this.getCollection(options.name);
}
upsertCollections(collections) {
for (const collection of this.sortCollectionsByInherits(collections)) {
if (this.collections.has(collection.name)) {
this.updateCollection(collection);
} else {
this.addCollection(collection);
}
}
}
sortCollectionsByInherits(collections) {
const map = /* @__PURE__ */ new Map();
for (const col of collections) {
map.set(col.name, col);
}
const graph = {};
const inDegree = {};
for (const col of collections) {
graph[col.name] = /* @__PURE__ */ new Set();
inDegree[col.name] = 0;
}
for (const col of collections) {
const inherits = col.inherits || [];
for (const parent of inherits) {
if (!graph[parent]) graph[parent] = /* @__PURE__ */ new Set();
graph[parent].add(col.name);
inDegree[col.name] = (inDegree[col.name] || 0) + 1;
}
}
const queue = [];
for (const name in inDegree) {
if (inDegree[name] === 0) queue.push(name);
}
const result = [];
while (queue.length) {
const curr = queue.shift();
if (map.has(curr)) {
result.push(map.get(curr));
}
for (const child of graph[curr] || []) {
inDegree[child]--;
if (inDegree[child] === 0) queue.push(child);
}
}
if (result.length !== collections.length) {
throw new Error("Collection inherits has circular dependency!");
}
return result;
}
getCollection(name) {
if (name.includes(".")) {
const [collectionName, fieldName] = name.split(".");
const collection = this.getCollection(collectionName);
if (!collection) {
throw new Error(`Collection ${collectionName} not found in data source ${this.dataSource.key}`);
}
const field = collection.getField(fieldName);
if (!field) {
throw new Error(`Field ${fieldName} not found in collection ${collectionName}`);
}
return field.targetCollection;
}
return this.collections.get(name);
}
getCollections() {
return import_lodash.default.sortBy(
Array.from(this.collections.values()).filter((collection) => !collection.hidden),
"sort"
);
}
clearCollections() {
this.collections.clear();
}
getAssociation(associationName) {
const [collectionName, fieldName] = associationName.split(".");
const collection = this.getCollection(collectionName);
if (!collection) {
throw new Error(`Collection ${collectionName} not found in data source ${this.dataSource.key}`);
}
return collection.getField(fieldName);
}
};
__name(_CollectionManager, "CollectionManager");
let CollectionManager = _CollectionManager;
const _Collection = class _Collection {
fields;
options;
inherits;
dataSource;
constructor(options = {}) {
this.options = (0, import_reactive.observable)({ ...options });
this.fields = import_reactive.observable.shallow(/* @__PURE__ */ new Map());
this.inherits = import_reactive.observable.shallow(/* @__PURE__ */ new Map());
this.setFields(options.fields || []);
}
getFilterByTK(record) {
if (!record) {
throw new Error("Record is required to get filterByTk");
}
if (Array.isArray(record)) {
return record.map((r) => this.getFilterByTK(r));
}
if (!this.filterTargetKey) {
throw new Error(`filterTargetKey is not defined for collection ${this.name}`);
}
if (typeof this.filterTargetKey === "string") {
return record[this.filterTargetKey];
}
return import_lodash.default.pick(record, this.filterTargetKey);
}
get hidden() {
return this.options.hidden || false;
}
get flowEngine() {
return this.dataSource.flowEngine;
}
get collectionManager() {
return this.dataSource.collectionManager;
}
get sort() {
return this.options.sort || 0;
}
get filterTargetKey() {
return this.options.filterTargetKey;
}
get dataSourceKey() {
return this.dataSource.key;
}
get name() {
return this.options.name;
}
get template() {
return this.options.template;
}
get storage() {
return this.options.storage || "local";
}
get title() {
return this.options.title ? this.flowEngine.translate(this.options.title) : this.name;
}
get titleCollectionField() {
const titleFieldName = this.options.titleField || this.filterTargetKey;
const titleCollectionField = this.getField(titleFieldName);
return titleCollectionField;
}
initInherits() {
this.inherits.clear();
for (const inherit of this.options.inherits || []) {
const collection = this.collectionManager.getCollection(inherit);
if (!collection) {
throw new Error(`Collection ${inherit} not found`);
}
this.inherits.set(inherit, collection);
}
}
setDataSource(dataSource) {
this.dataSource = dataSource;
}
setOptions(newOptions = {}) {
Object.keys(this.options).forEach((key) => delete this.options[key]);
Object.assign(this.options, newOptions);
this.initInherits();
this.upsertFields(this.options.fields || []);
}
getFields() {
const fieldMap = /* @__PURE__ */ new Map();
for (const inherit of this.inherits.values()) {
if (inherit && typeof inherit.getFields === "function") {
for (const field of inherit.getFields()) {
fieldMap.set(field.name, field);
}
}
}
for (const [name, field] of this.fields.entries()) {
fieldMap.set(name, field);
}
return Array.from(fieldMap.values());
}
getToOneAssociationFields() {
return this.getAssociationFields(["one"]);
}
getAssociationFields(types = []) {
if (types.includes("new")) {
return this.getFields().filter((field) => ["hasMany", "belongsToMany", "belongsToArray"].includes(field.type));
}
if (types.includes("many") && types.includes("one")) {
return this.getFields().filter((field) => field.isAssociationField());
}
if (types.includes("many")) {
return this.getFields().filter((field) => ["hasMany", "belongsToMany", "belongsToArray"].includes(field.type));
}
if (types.includes("one")) {
return this.getFields().filter((field) => ["hasOne", "belongsTo"].includes(field.type));
}
return this.getFields().filter((field) => field.isAssociationField());
}
mapFields(callback) {
return this.getFields().map(callback);
}
setFields(fields) {
this.fields.clear();
for (const field of fields) {
this.addField(field);
}
}
upsertFields(fields = []) {
for (const field of fields) {
if (this.fields.has(field.name)) {
this.fields.get(field.name).setOptions(field);
} else {
this.addField(field);
}
}
}
getFieldByPath(fieldPath) {
const [fieldName, ...otherKeys] = fieldPath.split(".");
const field = this.getField(fieldName);
if (otherKeys.length === 0) {
return field;
}
if (!field.targetCollection) {
return null;
}
return field.targetCollection.getFieldByPath(otherKeys.join("."));
}
getField(fieldName) {
return this.fields.get(fieldName);
}
getFullFieldPath(name) {
return this.dataSource.key + "." + this.name + "." + name;
}
addField(field) {
if (field.name && this.fields.has(field.name)) {
throw new Error(`Field with name ${field.name} already exists in collection ${this.name}`);
}
if (field instanceof CollectionField) {
field.setCollection(this);
this.fields.set(field.name, field);
} else {
const newField = new CollectionField(field);
newField.setCollection(this);
this.fields.set(newField.name, newField);
}
}
removeField(fieldName) {
return this.fields.delete(fieldName);
}
clearFields() {
return this.fields.clear();
}
refresh() {
}
/**
* 获取所有关联字段
* @returns 关联字段数组
*/
getRelationshipFields() {
const relationshipInterfaces = [
"o2o",
"oho",
"obo",
"m2o",
"createdBy",
"updatedBy",
"o2m",
"m2m",
"linkTo",
"chinaRegion",
"mbm"
];
return this.getFields().filter((field) => relationshipInterfaces.includes(field.interface));
}
/**
* 获取所有关联的集合
* @returns 关联集合数组
*/
getRelatedCollections() {
const relationshipFields = this.getRelationshipFields();
const relatedCollections = [];
const addedCollectionNames = /* @__PURE__ */ new Set();
for (const field of relationshipFields) {
if (field.target && !addedCollectionNames.has(field.target)) {
const targetCollection = this.collectionManager.getCollection(field.target);
if (targetCollection && targetCollection.name !== this.name) {
relatedCollections.push(targetCollection);
addedCollectionNames.add(field.target);
}
}
}
return relatedCollections;
}
/**
* 检查是否有关联字段
* @returns 是否有关联字段
*/
hasRelationshipFields() {
return this.getRelationshipFields().length > 0;
}
};
__name(_Collection, "Collection");
let Collection = _Collection;
const _CollectionField = class _CollectionField {
options;
collection;
constructor(options) {
this.options = (0, import_reactive.observable)({ ...options });
}
setOptions(newOptions = {}) {
Object.keys(this.options).forEach((key) => delete this.options[key]);
Object.assign(this.options, newOptions);
}
setCollection(collection) {
this.collection = collection;
}
get targetCollectionTitleFieldName() {
var _a, _b;
return (_b = (_a = this.targetCollection) == null ? void 0 : _a.titleCollectionField) == null ? void 0 : _b.name;
}
get targetCollectionTitleField() {
var _a;
return (_a = this.targetCollection) == null ? void 0 : _a.titleCollectionField;
}
get flowEngine() {
return this.collection.flowEngine;
}
get dataSourceKey() {
var _a;
return (_a = this.collection) == null ? void 0 : _a.dataSourceKey;
}
get resourceName() {
return `${this.collection.name}.${this.name}`;
}
get collectionName() {
var _a;
return ((_a = this.collection) == null ? void 0 : _a.name) || this.options.collectionName;
}
get readonly() {
var _a;
return this.options.readonly || ((_a = this.options.uiSchema) == null ? void 0 : _a["x-read-pretty"]) || false;
}
get fullpath() {
return this.collection.dataSource.key + "." + this.collection.name + "." + this.name;
}
get name() {
return this.options.name;
}
get type() {
return this.options.type;
}
get dataType() {
return this.options.dataType;
}
get foreignKey() {
return this.options.foreignKey;
}
get targetKey() {
return this.options.targetKey || this.targetCollection.filterTargetKey;
}
get sourceKey() {
return this.options.sourceKey;
}
get target() {
return this.options.target;
}
get title() {
var _a, _b, _c;
const titleValue = ((_a = this.options) == null ? void 0 : _a.title) || ((_c = (_b = this.options) == null ? void 0 : _b.uiSchema) == null ? void 0 : _c.title) || this.options.name;
return this.flowEngine.translate(titleValue);
}
set title(value) {
this.options.title = value;
}
get enum() {
var _a;
return ((_a = this.options.uiSchema) == null ? void 0 : _a.enum) || [];
}
get defaultValue() {
return this.options.defaultValue == null ? void 0 : this.options.defaultValue;
}
get interface() {
return this.options.interface;
}
get filterable() {
var _a;
return this.options.filterable || ((_a = this.getInterfaceOptions()) == null ? void 0 : _a.filterable);
}
get inputable() {
return this.options.inputable;
}
get uiSchema() {
return this.options.uiSchema || {};
}
get targetCollection() {
var _a;
return this.options.target && ((_a = this.collection) == null ? void 0 : _a.collectionManager.getCollection(this.options.target));
}
get validation() {
return this.options.validation;
}
getComponentProps() {
var _a;
const { type, target } = this.options;
const componentProps = import_lodash.default.omitBy(
{
...import_lodash.default.omit(((_a = this.options.uiSchema) == null ? void 0 : _a["x-component-props"]) || {}, "fieldNames"),
options: this.enum.length ? this.enum : void 0,
mode: this.type === "array" ? "multiple" : void 0,
multiple: target ? ["belongsToMany", "hasMany", "belongsToArray"].includes(type) : void 0,
maxCount: target && !["belongsToMany", "hasMany", "belongsToArray"].includes(type) ? 1 : void 0,
target
},
import_lodash.default.isUndefined
);
if (this.validation) {
const rules = [];
const schema = (0, import_jioToJoiSchema.jioToJoiSchema)(this.validation);
const label = this.title;
rules.push({
validator: /* @__PURE__ */ __name((_2, value) => {
const { error } = schema.validate(value, {
context: { label },
abortEarly: false
});
if (error) {
const message = error.details.map((d) => d.message.replace(/"value"/g, `"${label}"`)).join(", ");
return Promise.reject(message);
}
return Promise.resolve();
}, "validator")
});
componentProps.rules = rules;
}
return componentProps;
}
getFields() {
if (!this.options.target) {
return [];
}
if (!this.targetCollection) {
throw new Error(`Target collection ${this.options.target} not found for field ${this.name}`);
}
return this.targetCollection.getFields();
}
getInterfaceOptions() {
const app = this.flowEngine.context.app;
return app.dataSourceManager.collectionFieldInterfaceManager.getFieldInterface(this.interface);
}
getFilterOperators() {
var _a;
const opts = this.getInterfaceOptions();
return ((_a = opts == null ? void 0 : opts.filterable) == null ? void 0 : _a.operators) || [];
}
getSubclassesOf(baseClass) {
return this.flowEngine.getSubclassesOf(baseClass, (M, name) => {
const interfaceMatch = isFieldInterfaceMatch(M["supportedFieldInterfaces"], this.interface);
return interfaceMatch;
});
}
getFirstSubclassNameOf(baseClass) {
const subclasses = this.getSubclassesOf(baseClass);
for (const [name, M] of subclasses) {
if (M["supportedFieldInterfaces"] !== "*") {
return name;
}
}
return void 0;
}
isAssociationField() {
return ["belongsToMany", "belongsTo", "hasMany", "hasOne", "belongsToArray"].includes(this.type);
}
/**
* 检查字段是否为关联字段
* @returns 是否为关联字段
*/
isRelationshipField() {
const relationshipInterfaces = [
"o2o",
"oho",
"obo",
"m2o",
"createdBy",
"updatedBy",
"o2m",
"m2m",
"linkTo",
"chinaRegion",
"mbm"
];
return relationshipInterfaces.includes(this.interface);
}
};
__name(_CollectionField, "CollectionField");
let CollectionField = _CollectionField;
function isFieldInterfaceMatch(fieldInterfaces, targetInterface) {
if (!fieldInterfaces) return false;
if (fieldInterfaces === "*") return true;
if (typeof fieldInterfaces === "string") return fieldInterfaces === targetInterface;
if (Array.isArray(fieldInterfaces)) {
return fieldInterfaces.includes("*") || fieldInterfaces.includes(targetInterface);
}
return false;
}
__name(isFieldInterfaceMatch, "isFieldInterfaceMatch");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Collection,
CollectionField,
CollectionManager,
DataSource,
DataSourceManager,
isFieldInterfaceMatch,
jioToJoiSchema
});