@nocobase/plugin-workflow 1.8.0-beta.8 → 1.8.0

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.
Files changed (38) hide show
  1. package/dist/client/256475f279fb46d1.js +10 -0
  2. package/dist/client/48fc0fadf459229d.js +10 -0
  3. package/dist/client/69d4f948046f2ad2.js +10 -0
  4. package/dist/client/98ae71d4cff2436b.js +10 -0
  5. package/dist/client/WorkflowTasks.d.ts +16 -4
  6. package/dist/client/components/NotificationFieldset.d.ts +12 -0
  7. package/dist/client/components/UsersSelect.d.ts +19 -0
  8. package/dist/client/components/index.d.ts +1 -0
  9. package/dist/client/hooks/useWorkflowExecuted.d.ts +2 -2
  10. package/dist/client/index.d.ts +1 -1
  11. package/dist/client/index.js +1 -1
  12. package/dist/client/nodes/calculation.d.ts +1 -0
  13. package/dist/client/nodes/condition.d.ts +1 -0
  14. package/dist/client/schemas/executions.d.ts +1 -0
  15. package/dist/client/variable.d.ts +5 -2
  16. package/dist/common/collections/workflows.d.ts +14 -0
  17. package/dist/common/collections/workflows.js +1 -2
  18. package/dist/externalVersion.js +11 -10
  19. package/dist/locale/en-US.json +4 -1
  20. package/dist/locale/zh-CN.json +5 -1
  21. package/dist/node_modules/cron-parser/package.json +1 -1
  22. package/dist/node_modules/lru-cache/package.json +1 -1
  23. package/dist/node_modules/nodejs-snowflake/package.json +1 -1
  24. package/dist/server/Plugin.d.ts +3 -1
  25. package/dist/server/Plugin.js +58 -18
  26. package/dist/server/actions/nodes.js +3 -4
  27. package/dist/server/actions/workflows.js +11 -3
  28. package/dist/server/instructions/CalculationInstruction.d.ts +10 -0
  29. package/dist/server/instructions/CalculationInstruction.js +15 -0
  30. package/dist/server/instructions/ConditionInstruction.d.ts +11 -0
  31. package/dist/server/instructions/ConditionInstruction.js +15 -0
  32. package/dist/server/migrations/20250619213102-add-missed-stats.d.ts +14 -0
  33. package/dist/server/migrations/20250619213102-add-missed-stats.js +61 -0
  34. package/package.json +4 -3
  35. package/dist/client/3c67def1831d0b23.js +0 -10
  36. package/dist/client/8e2ad933533030aa.js +0 -10
  37. package/dist/client/a4111333ce86663c.js +0 -10
  38. package/dist/client/f9eeb46e5c6a9ffe.js +0 -10
@@ -78,4 +78,5 @@ export default class extends Instruction {
78
78
  label: any;
79
79
  };
80
80
  useInitializers(node: any): SchemaInitializerItemType;
81
+ testable: boolean;
81
82
  }
@@ -119,4 +119,5 @@ export default class extends Instruction {
119
119
  Component({ data }: {
120
120
  data: any;
121
121
  }): React.JSX.Element;
122
+ testable: boolean;
122
123
  }
@@ -152,6 +152,7 @@ export declare const executionSchema: {
152
152
  appends: string[];
153
153
  pageSize: number;
154
154
  sort: string[];
155
+ except: string[];
155
156
  filter: {};
156
157
  };
157
158
  };
@@ -7,6 +7,7 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import React from 'react';
10
+ import { CollectionManager } from '@nocobase/client';
10
11
  export type VariableOption = {
11
12
  key?: string;
12
13
  value?: string;
@@ -21,7 +22,9 @@ export type VariableDataType = 'boolean' | 'number' | 'string' | 'date' | {
21
22
  multiple?: boolean;
22
23
  entity?: boolean;
23
24
  };
24
- } | ((field: any) => boolean);
25
+ } | ((field: any, options: {
26
+ collectionManager?: CollectionManager;
27
+ }) => boolean);
25
28
  export type UseVariableOptions = {
26
29
  types?: VariableDataType[];
27
30
  fieldNames?: {
@@ -70,7 +73,7 @@ export declare const BaseTypeSets: {
70
73
  };
71
74
  export declare function useWorkflowVariableOptions(options?: UseVariableOptions): any[];
72
75
  export declare function getCollectionFieldOptions(options: any): VariableOption[];
73
- export declare function useGetCollectionFields(dataSourceName?: any): (collectionName: any) => import("@nocobase/client").CollectionFieldOptions[];
76
+ export declare function useGetDataSourceCollectionManager(dataSourceName?: any): CollectionManager;
74
77
  export declare function WorkflowVariableInput({ variableOptions, ...props }: {
75
78
  [x: string]: any;
76
79
  variableOptions: any;
@@ -223,6 +223,20 @@ declare const _default: {
223
223
  constraints?: undefined;
224
224
  through?: undefined;
225
225
  otherKey?: undefined;
226
+ } | {
227
+ type: string;
228
+ name: string;
229
+ target: string;
230
+ foreignKey: string;
231
+ sourceKey: string;
232
+ constraints: boolean;
233
+ interface?: undefined;
234
+ uiSchema?: undefined;
235
+ defaultValue?: undefined;
236
+ required?: undefined;
237
+ onDelete?: undefined;
238
+ through?: undefined;
239
+ otherKey?: undefined;
226
240
  } | {
227
241
  type: string;
228
242
  name: string;
@@ -170,8 +170,7 @@ var workflows_default = {
170
170
  target: "workflowStats",
171
171
  foreignKey: "key",
172
172
  sourceKey: "key",
173
- constraints: false,
174
- onDelete: "CASCADE"
173
+ constraints: false
175
174
  // interface: 'oho',
176
175
  // uiSchema: {
177
176
  // type: 'object',
@@ -11,8 +11,8 @@ module.exports = {
11
11
  "react": "18.2.0",
12
12
  "@formily/core": "2.3.0",
13
13
  "@formily/react": "2.3.0",
14
- "@nocobase/client": "1.8.0-beta.8",
15
- "@nocobase/utils": "1.8.0-beta.8",
14
+ "@nocobase/client": "1.8.0",
15
+ "@nocobase/utils": "1.8.0",
16
16
  "antd": "5.24.2",
17
17
  "@ant-design/icons": "5.6.1",
18
18
  "react-router-dom": "6.28.1",
@@ -20,16 +20,17 @@ module.exports = {
20
20
  "lodash": "4.17.21",
21
21
  "@dnd-kit/core": "6.1.0",
22
22
  "@formily/shared": "2.3.2",
23
+ "@nocobase/plugin-mobile": "1.8.0",
23
24
  "sequelize": "6.35.2",
24
- "@nocobase/database": "1.8.0-beta.8",
25
- "@nocobase/server": "1.8.0-beta.8",
26
- "@nocobase/data-source-manager": "1.8.0-beta.8",
27
- "@nocobase/logger": "1.8.0-beta.8",
28
- "@nocobase/evaluators": "1.8.0-beta.8",
25
+ "@nocobase/database": "1.8.0",
26
+ "@nocobase/server": "1.8.0",
27
+ "@nocobase/data-source-manager": "1.8.0",
28
+ "@nocobase/logger": "1.8.0",
29
+ "@nocobase/evaluators": "1.8.0",
29
30
  "@formily/antd-v5": "1.2.3",
30
31
  "@formily/reactive": "2.3.0",
31
- "@nocobase/actions": "1.8.0-beta.8",
32
+ "@nocobase/actions": "1.8.0",
32
33
  "dayjs": "1.11.13",
33
- "@nocobase/plugin-workflow-test": "1.8.0-beta.8",
34
- "@nocobase/test": "1.8.0-beta.8"
34
+ "@nocobase/plugin-workflow-test": "1.8.0",
35
+ "@nocobase/test": "1.8.0"
35
36
  };
@@ -209,5 +209,8 @@
209
209
  "Inside of branch": "Inside of branch",
210
210
  "Workflow todos": "Workflow todos",
211
211
  "New version enabled": "New version enabled",
212
- "Workflow is not exists": "Workflow is not exists"
212
+ "Workflow is not exists": "Workflow is not exists",
213
+ "Select users": "Select users",
214
+ "Query users": "Query users",
215
+ "Add": "Add"
213
216
  }
@@ -244,5 +244,9 @@
244
244
 
245
245
  "Workflow todos": "流程待办",
246
246
  "New version enabled": "已启用新版本",
247
- "Workflow is not exists": "工作流不存在"
247
+ "Workflow is not exists": "工作流不存在",
248
+
249
+ "Select users": "选择用户",
250
+ "Query users": "查询用户",
251
+ "Add": "添加"
248
252
  }
@@ -1 +1 @@
1
- {"name":"cron-parser","version":"4.4.0","description":"Node.js library for parsing crontab instructions","main":"lib/parser.js","types":"index.d.ts","typesVersions":{"<4.1":{"*":["types/ts3/*"]}},"directories":{"test":"test"},"scripts":{"test:tsd":"tsd","test:unit":"TZ=UTC tap ./test/*.js","test:cover":"TZ=UTC tap --coverage-report=html ./test/*.js","lint":"eslint .","lint:fix":"eslint --fix .","test":"npm run lint && npm run test:unit && npm run test:tsd"},"repository":{"type":"git","url":"https://github.com/harrisiirak/cron-parser.git"},"keywords":["cron","crontab","parser"],"author":"Harri Siirak","contributors":["Nicholas Clawson","Daniel Prentis <daniel@salsitasoft.com>","Renault John Lecoultre","Richard Astbury <richard.astbury@gmail.com>","Meaglin Wasabi <Meaglin.wasabi@gmail.com>","Mike Kusold <hello@mikekusold.com>","Alex Kit <alex.kit@atmajs.com>","Santiago Gimeno <santiago.gimeno@gmail.com>","Daniel <darc.tec@gmail.com>","Christian Steininger <christian.steininger.cs@gmail.com>","Mykola Piskovyi <m.piskovyi@gmail.com>","Brian Vaughn <brian.david.vaughn@gmail.com>","Nicholas Clawson <nickclaw@gmail.com>","Yasuhiroki <yasuhiroki.duck@gmail.com>","Nicholas Clawson <nickclaw@gmail.com>","Brendan Warkentin <faazshift@gmail.com>","Charlie Fish <fishcharlie.code@gmail.com>","Ian Graves <ian+diskimage@iangrav.es>","Andy Thompson <me@andytson.com>","Regev Brody <regevbr@gmail.com>"],"license":"MIT","dependencies":{"luxon":"^1.28.0"},"devDependencies":{"eslint":"^8.2.0","sinon":"^10.0.0","tap":"^16.0.1","tsd":"^0.19.0"},"engines":{"node":">=0.8"},"browser":{"fs":false},"tap":{"check-coverage":false},"tsd":{"directory":"test","compilerOptions":{"lib":["es2017","dom"]}},"_lastModified":"2025-06-16T09:49:24.180Z"}
1
+ {"name":"cron-parser","version":"4.4.0","description":"Node.js library for parsing crontab instructions","main":"lib/parser.js","types":"index.d.ts","typesVersions":{"<4.1":{"*":["types/ts3/*"]}},"directories":{"test":"test"},"scripts":{"test:tsd":"tsd","test:unit":"TZ=UTC tap ./test/*.js","test:cover":"TZ=UTC tap --coverage-report=html ./test/*.js","lint":"eslint .","lint:fix":"eslint --fix .","test":"npm run lint && npm run test:unit && npm run test:tsd"},"repository":{"type":"git","url":"https://github.com/harrisiirak/cron-parser.git"},"keywords":["cron","crontab","parser"],"author":"Harri Siirak","contributors":["Nicholas Clawson","Daniel Prentis <daniel@salsitasoft.com>","Renault John Lecoultre","Richard Astbury <richard.astbury@gmail.com>","Meaglin Wasabi <Meaglin.wasabi@gmail.com>","Mike Kusold <hello@mikekusold.com>","Alex Kit <alex.kit@atmajs.com>","Santiago Gimeno <santiago.gimeno@gmail.com>","Daniel <darc.tec@gmail.com>","Christian Steininger <christian.steininger.cs@gmail.com>","Mykola Piskovyi <m.piskovyi@gmail.com>","Brian Vaughn <brian.david.vaughn@gmail.com>","Nicholas Clawson <nickclaw@gmail.com>","Yasuhiroki <yasuhiroki.duck@gmail.com>","Nicholas Clawson <nickclaw@gmail.com>","Brendan Warkentin <faazshift@gmail.com>","Charlie Fish <fishcharlie.code@gmail.com>","Ian Graves <ian+diskimage@iangrav.es>","Andy Thompson <me@andytson.com>","Regev Brody <regevbr@gmail.com>"],"license":"MIT","dependencies":{"luxon":"^1.28.0"},"devDependencies":{"eslint":"^8.2.0","sinon":"^10.0.0","tap":"^16.0.1","tsd":"^0.19.0"},"engines":{"node":">=0.8"},"browser":{"fs":false},"tap":{"check-coverage":false},"tsd":{"directory":"test","compilerOptions":{"lib":["es2017","dom"]}},"_lastModified":"2025-07-07T11:16:51.530Z"}
@@ -1 +1 @@
1
- {"name":"lru-cache","description":"A cache object that deletes the least-recently-used items.","version":"8.0.5","author":"Isaac Z. Schlueter <i@izs.me>","keywords":["mru","lru","cache"],"sideEffects":false,"scripts":{"build":"npm run prepare","preprepare":"rm -rf dist","prepare":"tsc -p tsconfig.json && tsc -p tsconfig-esm.json","postprepare":"bash fixup.sh","pretest":"npm run prepare","presnap":"npm run prepare","test":"c8 tap","snap":"c8 tap","preversion":"npm test","postversion":"npm publish","prepublishOnly":"git push origin --follow-tags","format":"prettier --write .","typedoc":"typedoc --tsconfig tsconfig-esm.json ./src/*.ts","benchmark-results-typedoc":"bash scripts/benchmark-results-typedoc.sh","prebenchmark":"npm run prepare","benchmark":"make -C benchmark","preprofile":"npm run prepare","profile":"make -C benchmark profile"},"main":"./dist/cjs/index-cjs.js","module":"./dist/mjs/index.js","types":"./dist/mjs/index.d.ts","exports":{"./min":{"import":{"types":"./dist/mjs/index.d.ts","default":"./dist/mjs/index.min.js"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.min.js"}},".":{"import":{"types":"./dist/mjs/index.d.ts","default":"./dist/mjs/index.js"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index-cjs.js"}}},"repository":"git://github.com/isaacs/node-lru-cache.git","devDependencies":{"@size-limit/preset-small-lib":"^7.0.8","@types/node":"^17.0.31","@types/tap":"^15.0.6","benchmark":"^2.1.4","c8":"^7.11.2","clock-mock":"^1.0.6","esbuild":"^0.17.11","eslint-config-prettier":"^8.5.0","marked":"^4.2.12","mkdirp":"^2.1.5","prettier":"^2.6.2","size-limit":"^7.0.8","tap":"^16.3.4","ts-node":"^10.7.0","tslib":"^2.4.0","typedoc":"^0.23.24","typescript":"^4.6.4"},"license":"ISC","files":["dist"],"engines":{"node":">=16.14"},"prettier":{"semi":false,"printWidth":70,"tabWidth":2,"useTabs":false,"singleQuote":true,"jsxSingleQuote":false,"bracketSameLine":true,"arrowParens":"avoid","endOfLine":"lf"},"tap":{"coverage":false,"node-arg":["--expose-gc","--no-warnings","--loader","ts-node/esm"],"ts":false},"size-limit":[{"path":"./dist/mjs/index.js"}],"_lastModified":"2025-06-16T09:49:23.868Z"}
1
+ {"name":"lru-cache","description":"A cache object that deletes the least-recently-used items.","version":"8.0.5","author":"Isaac Z. Schlueter <i@izs.me>","keywords":["mru","lru","cache"],"sideEffects":false,"scripts":{"build":"npm run prepare","preprepare":"rm -rf dist","prepare":"tsc -p tsconfig.json && tsc -p tsconfig-esm.json","postprepare":"bash fixup.sh","pretest":"npm run prepare","presnap":"npm run prepare","test":"c8 tap","snap":"c8 tap","preversion":"npm test","postversion":"npm publish","prepublishOnly":"git push origin --follow-tags","format":"prettier --write .","typedoc":"typedoc --tsconfig tsconfig-esm.json ./src/*.ts","benchmark-results-typedoc":"bash scripts/benchmark-results-typedoc.sh","prebenchmark":"npm run prepare","benchmark":"make -C benchmark","preprofile":"npm run prepare","profile":"make -C benchmark profile"},"main":"./dist/cjs/index-cjs.js","module":"./dist/mjs/index.js","types":"./dist/mjs/index.d.ts","exports":{"./min":{"import":{"types":"./dist/mjs/index.d.ts","default":"./dist/mjs/index.min.js"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index.min.js"}},".":{"import":{"types":"./dist/mjs/index.d.ts","default":"./dist/mjs/index.js"},"require":{"types":"./dist/cjs/index.d.ts","default":"./dist/cjs/index-cjs.js"}}},"repository":"git://github.com/isaacs/node-lru-cache.git","devDependencies":{"@size-limit/preset-small-lib":"^7.0.8","@types/node":"^17.0.31","@types/tap":"^15.0.6","benchmark":"^2.1.4","c8":"^7.11.2","clock-mock":"^1.0.6","esbuild":"^0.17.11","eslint-config-prettier":"^8.5.0","marked":"^4.2.12","mkdirp":"^2.1.5","prettier":"^2.6.2","size-limit":"^7.0.8","tap":"^16.3.4","ts-node":"^10.7.0","tslib":"^2.4.0","typedoc":"^0.23.24","typescript":"^4.6.4"},"license":"ISC","files":["dist"],"engines":{"node":">=16.14"},"prettier":{"semi":false,"printWidth":70,"tabWidth":2,"useTabs":false,"singleQuote":true,"jsxSingleQuote":false,"bracketSameLine":true,"arrowParens":"avoid","endOfLine":"lf"},"tap":{"coverage":false,"node-arg":["--expose-gc","--no-warnings","--loader","ts-node/esm"],"ts":false},"size-limit":[{"path":"./dist/mjs/index.js"}],"_lastModified":"2025-07-07T11:16:51.184Z"}
@@ -1 +1 @@
1
- {"name":"nodejs-snowflake","collaborators":["Utkarsh Srivastava <utkarsh@sagacious.dev>"],"description":"Generate time sortable 64 bits unique ids for distributed systems (inspired from twitter snowflake)","version":"2.0.1","license":"Apache 2.0","repository":{"type":"git","url":"https://github.com/utkarsh-pro/nodejs-snowflake.git"},"files":["nodejs_snowflake_bg.wasm","nodejs_snowflake.js","nodejs_snowflake.d.ts"],"main":"nodejs_snowflake.js","types":"nodejs_snowflake.d.ts","_lastModified":"2025-06-16T09:49:23.637Z"}
1
+ {"name":"nodejs-snowflake","collaborators":["Utkarsh Srivastava <utkarsh@sagacious.dev>"],"description":"Generate time sortable 64 bits unique ids for distributed systems (inspired from twitter snowflake)","version":"2.0.1","license":"Apache 2.0","repository":{"type":"git","url":"https://github.com/utkarsh-pro/nodejs-snowflake.git"},"files":["nodejs_snowflake_bg.wasm","nodejs_snowflake.js","nodejs_snowflake.d.ts"],"main":"nodejs_snowflake.js","types":"nodejs_snowflake.d.ts","_lastModified":"2025-07-07T11:16:50.984Z"}
@@ -15,7 +15,7 @@ import Processor from './Processor';
15
15
  import { CustomFunction } from './functions';
16
16
  import Trigger from './triggers';
17
17
  import { InstructionInterface } from './instructions';
18
- import type { ExecutionModel, WorkflowModel } from './types';
18
+ import type { ExecutionModel, JobModel, WorkflowModel } from './types';
19
19
  type ID = number | string;
20
20
  export type EventOptions = {
21
21
  eventKey?: string;
@@ -41,6 +41,7 @@ export default class PluginWorkflowServer extends Plugin {
41
41
  private loggerCache;
42
42
  private meter;
43
43
  private checker;
44
+ private onQueueExecution;
44
45
  private onBeforeSave;
45
46
  private onAfterCreate;
46
47
  private onAfterUpdate;
@@ -74,6 +75,7 @@ export default class PluginWorkflowServer extends Plugin {
74
75
  private toggle;
75
76
  trigger(workflow: WorkflowModel, context: object, options?: EventOptions): void | Promise<Processor | null>;
76
77
  private triggerSync;
78
+ run(execution: ExecutionModel, job?: JobModel): Promise<void>;
77
79
  resume(job: any): Promise<void>;
78
80
  /**
79
81
  * Start a deferred execution
@@ -61,6 +61,7 @@ var import_DestroyInstruction = __toESM(require("./instructions/DestroyInstructi
61
61
  var import_QueryInstruction = __toESM(require("./instructions/QueryInstruction"));
62
62
  var import_UpdateInstruction = __toESM(require("./instructions/UpdateInstruction"));
63
63
  var import_WorkflowRepository = __toESM(require("./repositories/WorkflowRepository"));
64
+ const WORKER_JOB_WORKFLOW_PROCESS = "workflow:process";
64
65
  class PluginWorkflowServer extends import_server.Plugin {
65
66
  instructions = new import_utils.Registry();
66
67
  triggers = new import_utils.Registry();
@@ -75,6 +76,22 @@ class PluginWorkflowServer extends import_server.Plugin {
75
76
  loggerCache;
76
77
  meter = null;
77
78
  checker = null;
79
+ onQueueExecution = async (event) => {
80
+ const ExecutionRepo = this.db.getRepository("executions");
81
+ const execution = await ExecutionRepo.findOne({
82
+ filterByTk: event.executionId
83
+ });
84
+ if (!execution || execution.status !== import_constants.EXECUTION_STATUS.QUEUEING) {
85
+ this.getLogger("dispatcher").info(
86
+ `execution (${event.executionId}) from queue not found or not in queueing status, skip`
87
+ );
88
+ return;
89
+ }
90
+ this.getLogger(execution.workflowId).info(
91
+ `execution (${execution.id}) received from queue, adding to pending list`
92
+ );
93
+ this.run(execution);
94
+ };
78
95
  onBeforeSave = async (instance, { transaction, cycling }) => {
79
96
  if (cycling) {
80
97
  return;
@@ -176,6 +193,7 @@ class PluginWorkflowServer extends import_server.Plugin {
176
193
  this.ready = true;
177
194
  };
178
195
  onBeforeStop = async () => {
196
+ this.app.logger.info(`stopping workflow plugin before app (${this.app.name}) shutdown...`);
179
197
  for (const workflow of this.enabledCache.values()) {
180
198
  this.toggle(workflow, false, { silent: true });
181
199
  }
@@ -289,6 +307,12 @@ class PluginWorkflowServer extends import_server.Plugin {
289
307
  this.snowflake = new import_nodejs_snowflake.Snowflake({
290
308
  custom_epoch: pluginRecord == null ? void 0 : pluginRecord.createdAt.getTime()
291
309
  });
310
+ if (this.app.serving(WORKER_JOB_WORKFLOW_PROCESS)) {
311
+ this.app.backgroundJobManager.subscribe(`${this.name}.pendingExecution`, {
312
+ idle: () => !this.executing && !this.pending.length && !this.events.length,
313
+ process: this.onQueueExecution
314
+ });
315
+ }
292
316
  }
293
317
  /**
294
318
  * @internal
@@ -426,19 +450,25 @@ class PluginWorkflowServer extends import_server.Plugin {
426
450
  }
427
451
  return null;
428
452
  }
429
- async resume(job) {
430
- if (!job.execution) {
431
- job.execution = await job.getExecution();
432
- }
433
- this.getLogger(job.execution.workflowId).info(
434
- `execution (${job.execution.id}) resuming from job (${job.id}) added to pending list`
435
- );
436
- this.pending.push([job.execution, job]);
437
- if (this.executing) {
453
+ async run(execution, job) {
454
+ while (this.executing) {
438
455
  await this.executing;
439
456
  }
457
+ this.executing = this.process(execution, job);
458
+ await this.executing;
459
+ this.executing = null;
440
460
  this.dispatch();
441
461
  }
462
+ async resume(job) {
463
+ let { execution } = job;
464
+ if (!execution) {
465
+ execution = await job.getExecution();
466
+ }
467
+ this.getLogger(execution.workflowId).info(
468
+ `execution (${execution.id}) resuming from job (${job.id}) added to pending list`
469
+ );
470
+ this.run(execution, job);
471
+ }
442
472
  /**
443
473
  * Start a deferred execution
444
474
  * @experimental
@@ -448,11 +478,7 @@ class PluginWorkflowServer extends import_server.Plugin {
448
478
  return;
449
479
  }
450
480
  this.getLogger(execution.workflowId).info(`starting deferred execution (${execution.id})`);
451
- this.pending.push([execution]);
452
- if (this.executing) {
453
- await this.executing;
454
- }
455
- this.dispatch();
481
+ this.run(execution);
456
482
  }
457
483
  async validateEvent(workflow, context, options) {
458
484
  const trigger = this.triggers.get(workflow.type);
@@ -545,8 +571,14 @@ class PluginWorkflowServer extends import_server.Plugin {
545
571
  logger.info(`preparing execution for event`);
546
572
  try {
547
573
  const execution = await this.createExecution(...event);
548
- if ((execution == null ? void 0 : execution.status) === import_constants.EXECUTION_STATUS.QUEUEING && !this.executing && !this.pending.length) {
549
- this.pending.push([execution]);
574
+ if ((execution == null ? void 0 : execution.status) === import_constants.EXECUTION_STATUS.QUEUEING) {
575
+ if (!this.executing && !this.pending.length) {
576
+ logger.info(`local pending list is empty, adding execution (${execution.id}) to pending list`);
577
+ this.pending.push([execution]);
578
+ } else {
579
+ logger.info(`local pending list is not empty, sending execution (${execution.id}) to queue`);
580
+ this.app.backgroundJobManager.publish(`${this.name}.pendingExecution`, { executionId: execution.id });
581
+ }
550
582
  }
551
583
  } catch (error) {
552
584
  logger.error(`failed to create execution:`, { error });
@@ -566,6 +598,12 @@ class PluginWorkflowServer extends import_server.Plugin {
566
598
  this.getLogger("dispatcher").warn(`app is not ready, new dispatching will be ignored`);
567
599
  return;
568
600
  }
601
+ if (!this.app.serving(WORKER_JOB_WORKFLOW_PROCESS)) {
602
+ this.getLogger("dispatcher").warn(
603
+ `${WORKER_JOB_WORKFLOW_PROCESS} is not serving, new dispatching will be ignored`
604
+ );
605
+ return;
606
+ }
569
607
  if (this.executing) {
570
608
  this.getLogger("dispatcher").warn(`workflow executing is not finished, new dispatching will be ignored`);
571
609
  return;
@@ -587,7 +625,9 @@ class PluginWorkflowServer extends import_server.Plugin {
587
625
  async (transaction) => {
588
626
  const execution = await this.db.getRepository("executions").findOne({
589
627
  filter: {
590
- status: import_constants.EXECUTION_STATUS.QUEUEING,
628
+ status: {
629
+ [import_database.Op.is]: import_constants.EXECUTION_STATUS.QUEUEING
630
+ },
591
631
  "workflow.enabled": true
592
632
  },
593
633
  sort: "id",
@@ -631,7 +671,7 @@ class PluginWorkflowServer extends import_server.Plugin {
631
671
  if (execution.status === import_constants.EXECUTION_STATUS.QUEUEING) {
632
672
  const transaction = await this.useDataSourceTransaction("main", options.transaction);
633
673
  await execution.update({ status: import_constants.EXECUTION_STATUS.STARTED }, { transaction });
634
- logger.info(`queueing execution (${execution.id}) from pending list updated to started`);
674
+ logger.info(`execution (${execution.id}) from pending list updated to started`);
635
675
  }
636
676
  const processor = this.createProcessor(execution, options);
637
677
  logger.info(`execution (${execution.id}) ${job ? "resuming" : "starting"}...`);
@@ -52,8 +52,7 @@ async function create(context, next) {
52
52
  context.body = await db.sequelize.transaction(async (transaction) => {
53
53
  const workflow = await repository.getSourceModel(transaction);
54
54
  workflow.versionStats = await workflow.getVersionStats({ transaction });
55
- const { executed } = workflow.versionStats;
56
- if (executed) {
55
+ if (workflow.versionStats.executed > 0) {
57
56
  context.throw(400, "Node could not be created in executed workflow");
58
57
  }
59
58
  const instance = await repository.create({
@@ -147,7 +146,7 @@ async function destroy(context, next) {
147
146
  fields: [...fields, "workflowId"],
148
147
  appends: ["upstream", "downstream", "workflow.versionStats.executed"]
149
148
  });
150
- if (instance.workflow.versionStats.executed) {
149
+ if (instance.workflow.versionStats.executed > 0) {
151
150
  context.throw(400, "Nodes in executed workflow could not be deleted");
152
151
  }
153
152
  await db.sequelize.transaction(async (transaction) => {
@@ -207,7 +206,7 @@ async function update(context, next) {
207
206
  appends: ["workflow.versionStats.executed"],
208
207
  transaction
209
208
  });
210
- if (workflow.versionStats.executed) {
209
+ if (workflow.versionStats.executed > 0) {
211
210
  context.throw(400, "Nodes in executed workflow could not be reconfigured");
212
211
  }
213
212
  return repository.update({
@@ -57,7 +57,7 @@ async function update(context, next) {
57
57
  filterByTk,
58
58
  appends: ["versionStats"]
59
59
  });
60
- if (workflow.versionStats.executed) {
60
+ if (workflow.versionStats.executed > 0) {
61
61
  return context.throw(400, "config of executed workflow can not be updated");
62
62
  }
63
63
  }
@@ -84,11 +84,19 @@ async function destroy(context, next) {
84
84
  transaction
85
85
  });
86
86
  revisions.forEach((item) => ids.add(item.id));
87
- context.body = await repository.destroy({
87
+ const deleted = await repository.destroy({
88
88
  filterByTk: Array.from(ids),
89
89
  individualHooks: true,
90
90
  transaction
91
91
  });
92
+ const StatsRepo = context.db.getRepository("workflowStats");
93
+ await StatsRepo.destroy({
94
+ filter: {
95
+ key: Array.from(keysSet)
96
+ },
97
+ transaction
98
+ });
99
+ context.body = deleted;
92
100
  });
93
101
  next();
94
102
  }
@@ -150,7 +158,7 @@ async function execute(context, next) {
150
158
  filter: { key: workflow.key }
151
159
  });
152
160
  let newVersion;
153
- if (!executed && autoRevision) {
161
+ if (executed == 0 && autoRevision) {
154
162
  newVersion = await repository.revision({
155
163
  filterByTk: workflow.id,
156
164
  filter: { key: workflow.key },
@@ -21,5 +21,15 @@ export declare class CalculationInstruction extends Instruction {
21
21
  result: any;
22
22
  status: -2;
23
23
  }>;
24
+ test({ engine, expression }: {
25
+ engine?: string;
26
+ expression?: string;
27
+ }): Promise<{
28
+ result: any;
29
+ status: 1;
30
+ } | {
31
+ result: any;
32
+ status: -2;
33
+ }>;
24
34
  }
25
35
  export default CalculationInstruction;
@@ -51,6 +51,21 @@ class CalculationInstruction extends import__.Instruction {
51
51
  };
52
52
  }
53
53
  }
54
+ async test({ engine = "math.js", expression = "" }) {
55
+ const evaluator = import_evaluators.evaluators.get(engine);
56
+ try {
57
+ const result = evaluator && expression ? evaluator(expression) : null;
58
+ return {
59
+ result,
60
+ status: import_constants.JOB_STATUS.RESOLVED
61
+ };
62
+ } catch (e) {
63
+ return {
64
+ result: e.toString(),
65
+ status: import_constants.JOB_STATUS.ERROR
66
+ };
67
+ }
68
+ }
54
69
  }
55
70
  var CalculationInstruction_default = CalculationInstruction;
56
71
  // Annotate the CommonJS export names for ESM import in node:
@@ -29,5 +29,16 @@ export declare class ConditionInstruction extends Instruction {
29
29
  result: boolean;
30
30
  }>;
31
31
  resume(node: FlowNodeModel, branchJob: JobModel, processor: Processor): Promise<any>;
32
+ test({ engine, calculation, expression }: {
33
+ engine: any;
34
+ calculation: any;
35
+ expression?: string;
36
+ }): Promise<{
37
+ result: any;
38
+ status: 1;
39
+ } | {
40
+ result: any;
41
+ status: -2;
42
+ }>;
32
43
  }
33
44
  export default ConditionInstruction;
@@ -84,6 +84,21 @@ class ConditionInstruction extends import__.Instruction {
84
84
  }
85
85
  return processor.exit(branchJob.status);
86
86
  }
87
+ async test({ engine, calculation, expression = "" }) {
88
+ const evaluator = import_evaluators.evaluators.get(engine);
89
+ try {
90
+ const result = evaluator ? evaluator(expression) : (0, import_logicCalculate.logicCalculate)(calculation);
91
+ return {
92
+ result,
93
+ status: import_constants.JOB_STATUS.RESOLVED
94
+ };
95
+ } catch (e) {
96
+ return {
97
+ result: e.toString(),
98
+ status: import_constants.JOB_STATUS.ERROR
99
+ };
100
+ }
101
+ }
87
102
  }
88
103
  var ConditionInstruction_default = ConditionInstruction;
89
104
  // Annotate the CommonJS export names for ESM import in node:
@@ -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
+ import { Migration } from '@nocobase/server';
10
+ export default class extends Migration {
11
+ appVersion: string;
12
+ on: string;
13
+ up(): Promise<void>;
14
+ }
@@ -0,0 +1,61 @@
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 __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var add_missed_stats_exports = {};
28
+ __export(add_missed_stats_exports, {
29
+ default: () => add_missed_stats_default
30
+ });
31
+ module.exports = __toCommonJS(add_missed_stats_exports);
32
+ var import_server = require("@nocobase/server");
33
+ class add_missed_stats_default extends import_server.Migration {
34
+ appVersion = "<1.8.0";
35
+ on = "afterLoad";
36
+ async up() {
37
+ const { db } = this.context;
38
+ const WorkflowRepo = db.getRepository("workflows");
39
+ const ExecutionRepo = db.getRepository("executions");
40
+ await db.sequelize.transaction(async (transaction) => {
41
+ const workflows = await WorkflowRepo.find({
42
+ filter: {
43
+ current: true
44
+ },
45
+ appends: ["stats"],
46
+ transaction
47
+ });
48
+ for (const workflow of workflows) {
49
+ if (!workflow.stats) {
50
+ const executed = await ExecutionRepo.count({
51
+ filter: {
52
+ key: workflow.key
53
+ },
54
+ transaction
55
+ }) || workflow.allExecuted || 0;
56
+ await workflow.createStats({ executed }, { transaction });
57
+ }
58
+ }
59
+ });
60
+ }
61
+ }
package/package.json CHANGED
@@ -4,13 +4,13 @@
4
4
  "displayName.zh-CN": "工作流",
5
5
  "description": "A powerful BPM tool that provides foundational support for business automation, with the capability to extend unlimited triggers and nodes.",
6
6
  "description.zh-CN": "一个强大的 BPM 工具,为业务自动化提供基础支持,并且可任意扩展更多的触发器和节点。",
7
- "version": "1.8.0-beta.8",
7
+ "version": "1.8.0",
8
8
  "license": "AGPL-3.0",
9
9
  "main": "./dist/server/index.js",
10
10
  "homepage": "https://docs.nocobase.com/handbook/workflow",
11
11
  "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/workflow",
12
12
  "dependencies": {
13
- "@nocobase/plugin-workflow-test": "1.8.0-beta.8"
13
+ "@nocobase/plugin-workflow-test": "1.8.0"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@ant-design/icons": "5.x",
@@ -40,13 +40,14 @@
40
40
  "@nocobase/logger": "1.x",
41
41
  "@nocobase/plugin-data-source-main": "1.x",
42
42
  "@nocobase/plugin-error-handler": "1.x",
43
+ "@nocobase/plugin-mobile": "1.x",
43
44
  "@nocobase/plugin-users": "1.x",
44
45
  "@nocobase/resourcer": "1.x",
45
46
  "@nocobase/server": "1.x",
46
47
  "@nocobase/test": "1.x",
47
48
  "@nocobase/utils": "1.x"
48
49
  },
49
- "gitHead": "b8871f494a7c9f21f7ed474a4e4fb3ccc38c23dd",
50
+ "gitHead": "043de5b0f70cbeac2a0f83e992eaa0e09c90d53d",
50
51
  "keywords": [
51
52
  "Workflow"
52
53
  ]