UNPKG

@nocobase/flow-engine

Version:

A standalone flow engine for NocoBase, managing workflows, models, and actions.

734 lines (732 loc) 23.2 kB
/** * 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 });