@nocobase/plugin-workflow 1.4.20 → 1.4.22

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.
@@ -11,24 +11,24 @@ 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.4.20",
15
- "@nocobase/utils": "1.4.20",
14
+ "@nocobase/client": "1.4.22",
15
+ "@nocobase/utils": "1.4.22",
16
16
  "antd": "5.12.8",
17
17
  "@ant-design/icons": "5.2.6",
18
18
  "react-router-dom": "6.21.0",
19
19
  "react-i18next": "11.18.6",
20
20
  "@formily/shared": "2.3.2",
21
21
  "lodash": "4.17.21",
22
- "@nocobase/database": "1.4.20",
23
- "@nocobase/server": "1.4.20",
24
- "@nocobase/logger": "1.4.20",
25
- "@nocobase/evaluators": "1.4.20",
22
+ "sequelize": "6.35.2",
23
+ "@nocobase/database": "1.4.22",
24
+ "@nocobase/server": "1.4.22",
25
+ "@nocobase/logger": "1.4.22",
26
+ "@nocobase/evaluators": "1.4.22",
26
27
  "@formily/antd-v5": "1.1.9",
27
28
  "@formily/reactive": "2.3.0",
28
- "@nocobase/actions": "1.4.20",
29
+ "@nocobase/actions": "1.4.22",
29
30
  "dayjs": "1.11.10",
30
- "@nocobase/data-source-manager": "1.4.20",
31
- "sequelize": "6.35.2",
32
- "@nocobase/plugin-workflow-test": "1.4.20",
33
- "@nocobase/test": "1.4.20"
31
+ "@nocobase/data-source-manager": "1.4.22",
32
+ "@nocobase/plugin-workflow-test": "1.4.22",
33
+ "@nocobase/test": "1.4.22"
34
34
  };
@@ -143,19 +143,19 @@
143
143
  "Node type": "节点类型",
144
144
  "Unknown node": "未知节点",
145
145
  "Node with unknown type will cause error. Please delete it or check plugin which provide this type.": "未知类型的节点会导致错误,请删除或检查提供该类型的插件。",
146
- "Calculation": "运算",
147
- "Calculation engine": "运算引擎",
146
+ "Calculation": "计算",
147
+ "Calculation engine": "计算引擎",
148
148
  "Basic": "基础",
149
- "Calculation expression": "运算表达式",
149
+ "Calculation expression": "计算表达式",
150
150
  "Expression syntax error": "表达式语法错误",
151
151
  "Syntax references: ": "语法参考:",
152
- "Calculation result": "运算结果",
152
+ "Calculation result": "计算结果",
153
153
  "True": "真",
154
154
  "False": "假",
155
155
  "concat": "连接",
156
156
  "Condition": "条件判断",
157
157
  "Based on boolean result of the calculation to determine whether to \"continue\" or \"exit\" the process, or continue on different branches of \"yes\" and \"no\".":
158
- "基于运算结果的真假来决定“继续”或“退出”流程,或者在“是”与“否”的分支上分别继续。",
158
+ "基于计算结果的真假来决定“继续”或“退出”流程,或者在“是”与“否”的分支上分别继续。",
159
159
  "Mode": "模式",
160
160
  "Continue when \"Yes\"": "“是”则继续",
161
161
  "Branch into \"Yes\" and \"No\"": "“是”和“否”分别继续",
@@ -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-01-09T04:57:35.001Z"}
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-01-10T16:35:10.654Z"}
@@ -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-01-09T04:57:34.634Z"}
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-01-10T16:35:10.302Z"}
@@ -6,7 +6,7 @@
6
6
  * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
- import { Transactionable } from '@nocobase/database';
9
+ import { Transactionable } from 'sequelize';
10
10
  import { Plugin } from '@nocobase/server';
11
11
  import { Registry } from '@nocobase/utils';
12
12
  import { Logger } from '@nocobase/logger';
@@ -40,7 +40,7 @@ export default class PluginWorkflowServer extends Plugin {
40
40
  /**
41
41
  * @experimental
42
42
  */
43
- getLogger(workflowId: ID): Logger;
43
+ getLogger(workflowId?: ID): Logger;
44
44
  /**
45
45
  * @experimental
46
46
  * @param {WorkflowModel} workflow
@@ -67,11 +67,11 @@ export default class PluginWorkflowServer extends Plugin {
67
67
  * Start a deferred execution
68
68
  * @experimental
69
69
  */
70
- start(execution: ExecutionModel): void;
71
- createProcessor(execution: ExecutionModel, options?: {}): Processor;
70
+ start(execution: ExecutionModel): Promise<void>;
72
71
  private createExecution;
73
72
  private prepare;
74
73
  private dispatch;
74
+ createProcessor(execution: ExecutionModel, options?: {}): Processor;
75
75
  private process;
76
76
  /**
77
77
  * @experimental
@@ -41,6 +41,7 @@ __export(Plugin_exports, {
41
41
  module.exports = __toCommonJS(Plugin_exports);
42
42
  var import_path = __toESM(require("path"));
43
43
  var import_crypto = require("crypto");
44
+ var import_sequelize = require("sequelize");
44
45
  var import_lru_cache = __toESM(require("lru-cache"));
45
46
  var import_database = require("@nocobase/database");
46
47
  var import_server = require("@nocobase/server");
@@ -137,7 +138,7 @@ class PluginWorkflowServer extends import_server.Plugin {
137
138
  /**
138
139
  * @experimental
139
140
  */
140
- getLogger(workflowId) {
141
+ getLogger(workflowId = "dispatcher") {
141
142
  const now = /* @__PURE__ */ new Date();
142
143
  const date = `${now.getFullYear()}-${`0${now.getMonth() + 1}`.slice(-2)}-${`0${now.getDate()}`.slice(-2)}`;
143
144
  const key = `${date}-${workflowId}}`;
@@ -256,7 +257,6 @@ class PluginWorkflowServer extends import_server.Plugin {
256
257
  db.on("workflows.afterUpdate", (model) => this.toggle(model));
257
258
  db.on("workflows.beforeDestroy", (model) => this.toggle(model, false));
258
259
  this.app.on("afterStart", async () => {
259
- this.app.setMaintainingMessage("check for not started executions");
260
260
  this.ready = true;
261
261
  const collection = db.getCollection("workflows");
262
262
  const workflows = await collection.repository.find({
@@ -266,8 +266,10 @@ class PluginWorkflowServer extends import_server.Plugin {
266
266
  this.toggle(workflow);
267
267
  });
268
268
  this.checker = setInterval(() => {
269
+ this.getLogger("dispatcher").info(`(cycling) check for queueing executions`);
269
270
  this.dispatch();
270
271
  }, 3e5);
272
+ this.getLogger("dispatcher").info("(starting) check for queueing executions");
271
273
  this.dispatch();
272
274
  });
273
275
  this.app.on("beforeStop", async () => {
@@ -320,6 +322,15 @@ class PluginWorkflowServer extends import_server.Plugin {
320
322
  logger.debug(`ignored event data:`, context);
321
323
  return;
322
324
  }
325
+ const duplicated = this.events.find(([w, c, { eventKey }]) => {
326
+ if (eventKey && options.eventKey) {
327
+ return eventKey === options.eventKey;
328
+ }
329
+ });
330
+ if (duplicated) {
331
+ logger.warn(`event of workflow ${workflow.id} is duplicated, event will be ignored`);
332
+ return;
333
+ }
323
334
  if (context == null) {
324
335
  logger.warn(`workflow ${workflow.id} event data context is null, event will be ignored`);
325
336
  return;
@@ -333,6 +344,7 @@ class PluginWorkflowServer extends import_server.Plugin {
333
344
  logger.info(`new event triggered, now events: ${this.events.length}`);
334
345
  logger.debug(`event data:`, { context });
335
346
  if (this.events.length > 1) {
347
+ logger.info(`new event is pending to be prepared after previous preparation is finished`);
336
348
  return;
337
349
  }
338
350
  setTimeout(this.prepare);
@@ -360,22 +372,25 @@ class PluginWorkflowServer extends import_server.Plugin {
360
372
  `execution (${job.execution.id}) resuming from job (${job.id}) added to pending list`
361
373
  );
362
374
  this.pending.push([job.execution, job]);
375
+ if (this.executing) {
376
+ await this.executing;
377
+ }
363
378
  this.dispatch();
364
379
  }
365
380
  /**
366
381
  * Start a deferred execution
367
382
  * @experimental
368
383
  */
369
- start(execution) {
384
+ async start(execution) {
370
385
  if (execution.status !== import_constants.EXECUTION_STATUS.STARTED) {
371
386
  return;
372
387
  }
373
388
  this.pending.push([execution]);
389
+ if (this.executing) {
390
+ await this.executing;
391
+ }
374
392
  this.dispatch();
375
393
  }
376
- createProcessor(execution, options = {}) {
377
- return new import_Processor.default(execution, { ...options, plugin: this });
378
- }
379
394
  async createExecution(workflow, context, options) {
380
395
  const { deferred } = options;
381
396
  const transaction = await this.useDataSourceTransaction("main", options.transaction, true);
@@ -434,7 +449,7 @@ class PluginWorkflowServer extends import_server.Plugin {
434
449
  const event = this.events.shift();
435
450
  this.eventsCount = this.events.length;
436
451
  if (!event) {
437
- this.getLogger("dispatcher").warn(`events queue is empty, no need to prepare`);
452
+ this.getLogger("dispatcher").info(`events queue is empty, no need to prepare`);
438
453
  return;
439
454
  }
440
455
  const logger = this.getLogger(event[0].id);
@@ -467,38 +482,56 @@ class PluginWorkflowServer extends import_server.Plugin {
467
482
  }
468
483
  this.executing = (async () => {
469
484
  let next = null;
470
- try {
471
- if (this.pending.length) {
472
- next = this.pending.shift();
473
- this.getLogger(next[0].workflowId).info(`pending execution (${next[0].id}) ready to process`);
474
- } else {
475
- const execution = await this.db.getRepository("executions").findOne({
476
- filter: {
477
- status: import_constants.EXECUTION_STATUS.QUEUEING,
478
- "workflow.enabled": true,
479
- "workflow.id": {
480
- [import_database.Op.not]: null
481
- }
485
+ if (this.pending.length) {
486
+ next = this.pending.shift();
487
+ this.getLogger(next[0].workflowId).info(`pending execution (${next[0].id}) ready to process`);
488
+ } else {
489
+ try {
490
+ await this.db.sequelize.transaction(
491
+ {
492
+ isolationLevel: this.db.options.dialect === "sqlite" ? [][0] : import_sequelize.Transaction.ISOLATION_LEVELS.REPEATABLE_READ
482
493
  },
483
- appends: ["workflow"],
484
- sort: "id"
485
- });
486
- if (execution) {
487
- this.getLogger(execution.workflowId).info(`execution (${execution.id}) fetched from db`);
488
- next = [execution];
489
- }
490
- }
491
- if (next) {
492
- await this.process(...next);
493
- }
494
- } finally {
495
- this.executing = null;
496
- if (next) {
497
- this.dispatch();
494
+ async (transaction) => {
495
+ const execution = await this.db.getRepository("executions").findOne({
496
+ filter: {
497
+ status: import_constants.EXECUTION_STATUS.QUEUEING,
498
+ "workflow.enabled": true
499
+ },
500
+ sort: "id",
501
+ transaction
502
+ });
503
+ if (execution) {
504
+ this.getLogger(execution.workflowId).info(`execution (${execution.id}) fetched from db`);
505
+ await execution.update(
506
+ {
507
+ status: import_constants.EXECUTION_STATUS.STARTED
508
+ },
509
+ { transaction }
510
+ );
511
+ execution.workflow = this.enabledCache.get(execution.workflowId);
512
+ next = [execution];
513
+ } else {
514
+ this.getLogger("dispatcher").info(`no execution in db queued to process`);
515
+ }
516
+ }
517
+ );
518
+ } catch (error) {
519
+ this.getLogger("dispatcher").error(`fetching execution from db failed: ${error.message}`, { error });
498
520
  }
499
521
  }
522
+ if (next) {
523
+ await this.process(...next);
524
+ }
525
+ this.executing = null;
526
+ if (next) {
527
+ this.getLogger("dispatcher").info(`last process finished, will do another dispatch`);
528
+ this.dispatch();
529
+ }
500
530
  })();
501
531
  }
532
+ createProcessor(execution, options = {}) {
533
+ return new import_Processor.default(execution, { ...options, plugin: this });
534
+ }
502
535
  async process(execution, job, options = {}) {
503
536
  var _a, _b;
504
537
  if (execution.status === import_constants.EXECUTION_STATUS.QUEUEING) {
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.4.20",
7
+ "version": "1.4.22",
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.4.20"
13
+ "@nocobase/plugin-workflow-test": "1.4.22"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@ant-design/icons": "5.x",
@@ -44,7 +44,7 @@
44
44
  "@nocobase/test": "1.x",
45
45
  "@nocobase/utils": "1.x"
46
46
  },
47
- "gitHead": "13b73921752eeadfd75993ec4088c860edfefc8f",
47
+ "gitHead": "647ab1cb179342ca04eb1a2eeb7781c423ffc3fd",
48
48
  "keywords": [
49
49
  "Workflow"
50
50
  ]