@nocobase/plugin-workflow 1.7.0-beta.9 → 1.8.0-alpha.1

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 (95) hide show
  1. package/dist/client/602ab29d66b356c4.js +10 -0
  2. package/dist/client/77f05f24f6a5f421.js +10 -0
  3. package/dist/client/7af2da3defaae26f.js +10 -0
  4. package/dist/client/WorkflowCategoryTabs.d.ts +10 -0
  5. package/dist/client/WorkflowCollectionsProvider.d.ts +10 -0
  6. package/dist/client/WorkflowTasks.d.ts +12 -3
  7. package/dist/client/components/EmunerationField.d.ts +9 -0
  8. package/dist/client/e03d641492559086.js +10 -0
  9. package/dist/client/index.d.ts +1 -0
  10. package/dist/client/index.js +1 -1
  11. package/dist/client/locale/index.d.ts +2 -1
  12. package/dist/client/schemas/executions.d.ts +126 -125
  13. package/dist/client/triggers/schedule/RepeatField.d.ts +2 -1
  14. package/dist/common/collections/executions.d.ts +136 -0
  15. package/dist/common/collections/executions.js +125 -0
  16. package/dist/common/collections/flow_nodes.d.ts +65 -0
  17. package/dist/common/collections/flow_nodes.js +94 -0
  18. package/dist/common/collections/jobs.d.ts +37 -0
  19. package/dist/common/collections/jobs.js +74 -0
  20. package/dist/common/collections/userWorkflowTasks.d.ts +37 -0
  21. package/dist/common/collections/userWorkflowTasks.js +65 -0
  22. package/dist/common/collections/workflowCategories.d.ts +65 -0
  23. package/dist/common/collections/workflowCategories.js +68 -0
  24. package/dist/common/collections/workflowCategoryRelations.d.ts +21 -0
  25. package/dist/common/collections/workflowCategoryRelations.js +51 -0
  26. package/dist/common/collections/workflowStats.d.ts +37 -0
  27. package/dist/common/collections/workflowStats.js +59 -0
  28. package/dist/common/collections/workflowTasks.d.ts +10 -0
  29. package/dist/common/collections/workflowTasks.js +64 -0
  30. package/dist/common/collections/workflowVersionStats.d.ts +37 -0
  31. package/dist/common/collections/workflowVersionStats.js +59 -0
  32. package/dist/common/collections/workflows.d.ts +263 -0
  33. package/dist/common/collections/workflows.js +244 -0
  34. package/dist/common/constants.d.ts +9 -0
  35. package/dist/{server/actions/workflowTasks.js → common/constants.js} +6 -17
  36. package/dist/externalVersion.js +12 -11
  37. package/dist/locale/en-US.json +127 -15
  38. package/dist/locale/zh-CN.json +10 -1
  39. package/dist/node_modules/cron-parser/package.json +1 -1
  40. package/dist/node_modules/lru-cache/package.json +1 -1
  41. package/dist/node_modules/nodejs-snowflake/LICENSE +201 -0
  42. package/dist/node_modules/nodejs-snowflake/nodejs_snowflake.d.ts +62 -0
  43. package/dist/node_modules/nodejs-snowflake/nodejs_snowflake.js +1 -0
  44. package/dist/node_modules/nodejs-snowflake/nodejs_snowflake_bg.wasm +0 -0
  45. package/dist/node_modules/nodejs-snowflake/package.json +1 -0
  46. package/dist/server/Dispatcher.d.ts +11 -0
  47. package/dist/server/Dispatcher.js +35 -0
  48. package/dist/server/Plugin.d.ts +13 -2
  49. package/dist/server/Plugin.js +170 -107
  50. package/dist/server/Processor.d.ts +4 -11
  51. package/dist/server/Processor.js +50 -45
  52. package/dist/server/actions/index.js +2 -2
  53. package/dist/server/actions/nodes.js +7 -5
  54. package/dist/server/actions/{workflowTasks.d.ts → userWorkflowTasks.d.ts} +1 -1
  55. package/dist/server/actions/userWorkflowTasks.js +54 -0
  56. package/dist/server/actions/workflows.js +6 -3
  57. package/dist/server/collections/executions.js +12 -44
  58. package/dist/server/collections/flow_nodes.js +12 -57
  59. package/dist/server/collections/jobs.js +12 -36
  60. package/dist/server/collections/userWorkflowTasks.d.ts +11 -0
  61. package/dist/server/collections/userWorkflowTasks.js +43 -0
  62. package/dist/server/collections/workflowCategories.d.ts +11 -0
  63. package/dist/server/collections/workflowCategories.js +43 -0
  64. package/dist/server/collections/workflowCategoryRelations.d.ts +11 -0
  65. package/dist/server/collections/workflowCategoryRelations.js +43 -0
  66. package/dist/server/collections/workflowStats.d.ts +11 -0
  67. package/dist/server/collections/workflowStats.js +43 -0
  68. package/dist/server/collections/workflowTasks.d.ts +2 -1
  69. package/dist/server/collections/workflowTasks.js +12 -33
  70. package/dist/server/collections/workflowVersionStats.d.ts +11 -0
  71. package/dist/server/collections/workflowVersionStats.js +43 -0
  72. package/dist/server/collections/workflows.d.ts +2 -1
  73. package/dist/server/collections/workflows.js +12 -101
  74. package/dist/server/migrations/20250320223415-stats.d.ts +14 -0
  75. package/dist/server/migrations/20250320223415-stats.js +82 -0
  76. package/dist/server/migrations/20250409164913-remove-jobs-auto-increment.d.ts +14 -0
  77. package/dist/server/migrations/20250409164913-remove-jobs-auto-increment.js +57 -0
  78. package/dist/server/repositories/WorkflowRepository.js +3 -2
  79. package/dist/server/triggers/CollectionTrigger.js +3 -2
  80. package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.d.ts +5 -3
  81. package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.js +39 -36
  82. package/dist/server/triggers/ScheduleTrigger/StaticScheduleTrigger.d.ts +4 -2
  83. package/dist/server/triggers/ScheduleTrigger/StaticScheduleTrigger.js +26 -24
  84. package/dist/server/triggers/ScheduleTrigger/index.d.ts +2 -1
  85. package/dist/server/triggers/ScheduleTrigger/index.js +4 -8
  86. package/dist/server/triggers/index.d.ts +1 -1
  87. package/dist/server/types/Workflow.d.ts +0 -2
  88. package/dist/swagger/index.d.ts +0 -14
  89. package/dist/swagger/index.js +0 -14
  90. package/package.json +6 -4
  91. package/dist/client/739d458621edf81f.js +0 -10
  92. package/dist/client/8e96ce6ed324ce69.js +0 -10
  93. package/dist/client/c107ec5004b8644b.js +0 -10
  94. package/dist/client/c3f36ae11fcc489e.js +0 -10
  95. package/dist/client/nodes/output.d.ts +0 -31
@@ -6,6 +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 { Snowflake } from 'nodejs-snowflake';
9
10
  import { Transactionable } from 'sequelize';
10
11
  import { Plugin } from '@nocobase/server';
11
12
  import { Registry } from '@nocobase/utils';
@@ -14,7 +15,7 @@ import Processor from './Processor';
14
15
  import { CustomFunction } from './functions';
15
16
  import Trigger from './triggers';
16
17
  import { InstructionInterface } from './instructions';
17
- import type { ExecutionModel, WorkflowModel, WorkflowTaskModel } from './types';
18
+ import type { ExecutionModel, WorkflowModel } from './types';
18
19
  type ID = number | string;
19
20
  export type EventOptions = {
20
21
  eventKey?: string;
@@ -31,6 +32,7 @@ export default class PluginWorkflowServer extends Plugin {
31
32
  triggers: Registry<Trigger>;
32
33
  functions: Registry<CustomFunction>;
33
34
  enabledCache: Map<number, WorkflowModel>;
35
+ snowflake: Snowflake;
34
36
  private ready;
35
37
  private executing;
36
38
  private pending;
@@ -39,7 +41,13 @@ export default class PluginWorkflowServer extends Plugin {
39
41
  private loggerCache;
40
42
  private meter;
41
43
  private checker;
44
+ private onQueueExecution;
42
45
  private onBeforeSave;
46
+ private onAfterCreate;
47
+ private onAfterUpdate;
48
+ private onAfterDestroy;
49
+ private onAfterStart;
50
+ private onBeforeStop;
43
51
  handleSyncMessage(message: any): Promise<void>;
44
52
  /**
45
53
  * @experimental
@@ -91,6 +99,9 @@ export default class PluginWorkflowServer extends Plugin {
91
99
  /**
92
100
  * @experimental
93
101
  */
94
- toggleTaskStatus(task: WorkflowTaskModel, done: boolean, { transaction }: Transactionable): Promise<void>;
102
+ updateTasksStats(userId: number, type: string, stats: {
103
+ pending: number;
104
+ all: number;
105
+ }, { transaction }: Transactionable): Promise<void>;
95
106
  }
96
107
  export {};
@@ -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_nodejs_snowflake = require("nodejs-snowflake");
44
45
  var import_sequelize = require("sequelize");
45
46
  var import_lru_cache = __toESM(require("lru-cache"));
46
47
  var import_database = require("@nocobase/database");
@@ -60,12 +61,12 @@ var import_DestroyInstruction = __toESM(require("./instructions/DestroyInstructi
60
61
  var import_QueryInstruction = __toESM(require("./instructions/QueryInstruction"));
61
62
  var import_UpdateInstruction = __toESM(require("./instructions/UpdateInstruction"));
62
63
  var import_WorkflowRepository = __toESM(require("./repositories/WorkflowRepository"));
63
- var import_WorkflowTasksRepository = __toESM(require("./repositories/WorkflowTasksRepository"));
64
64
  class PluginWorkflowServer extends import_server.Plugin {
65
65
  instructions = new import_utils.Registry();
66
66
  triggers = new import_utils.Registry();
67
67
  functions = new import_utils.Registry();
68
68
  enabledCache = /* @__PURE__ */ new Map();
69
+ snowflake;
69
70
  ready = false;
70
71
  executing = null;
71
72
  pending = [];
@@ -74,8 +75,28 @@ class PluginWorkflowServer extends import_server.Plugin {
74
75
  loggerCache;
75
76
  meter = null;
76
77
  checker = null;
77
- onBeforeSave = async (instance, { transaction }) => {
78
+ onQueueExecution = async (event) => {
79
+ const ExecutionRepo = this.db.getRepository("executions");
80
+ const execution = await ExecutionRepo.findOne({
81
+ filterByTk: event.executionId
82
+ });
83
+ if (!execution || execution.status !== import_constants.EXECUTION_STATUS.QUEUEING) {
84
+ return;
85
+ }
86
+ this.getLogger(execution.workflowId).info(
87
+ `execution (${execution.id}) received from queue, adding to pending list`
88
+ );
89
+ this.pending.push([execution]);
90
+ this.dispatch();
91
+ };
92
+ onBeforeSave = async (instance, { transaction, cycling }) => {
93
+ if (cycling) {
94
+ return;
95
+ }
78
96
  const Model = instance.constructor;
97
+ if (!instance.key) {
98
+ instance.set("key", (0, import_utils.uid)());
99
+ }
79
100
  if (instance.enabled) {
80
101
  instance.set("current", true);
81
102
  }
@@ -91,18 +112,95 @@ class PluginWorkflowServer extends import_server.Plugin {
91
112
  });
92
113
  if (!previous) {
93
114
  instance.set("current", true);
94
- }
95
- if (instance.current && previous) {
115
+ } else if (instance.current) {
96
116
  await previous.update(
97
117
  { enabled: false, current: null },
98
118
  {
99
119
  transaction,
100
- hooks: false
120
+ cycling: true
101
121
  }
102
122
  );
103
123
  this.toggle(previous, false, { transaction });
104
124
  }
105
125
  };
126
+ onAfterCreate = async (model, { transaction }) => {
127
+ const WorkflowStatsModel = this.db.getModel("workflowStats");
128
+ let stats = await WorkflowStatsModel.findOne({
129
+ where: { key: model.key },
130
+ transaction
131
+ });
132
+ if (!stats) {
133
+ stats = await model.createStats({ executed: 0 }, { transaction });
134
+ }
135
+ model.stats = stats;
136
+ model.versionStats = await model.createVersionStats({ id: model.id }, { transaction });
137
+ if (model.enabled) {
138
+ this.toggle(model, true, { transaction });
139
+ }
140
+ };
141
+ onAfterUpdate = async (model, { transaction }) => {
142
+ model.stats = await model.getStats({ transaction });
143
+ model.versionStats = await model.getVersionStats({ transaction });
144
+ this.toggle(model, model.enabled, { transaction });
145
+ };
146
+ onAfterDestroy = async (model, { transaction }) => {
147
+ this.toggle(model, false, { transaction });
148
+ const TaskRepo = this.db.getRepository("workflowTasks");
149
+ await TaskRepo.destroy({
150
+ filter: {
151
+ workflowId: model.id
152
+ },
153
+ transaction
154
+ });
155
+ };
156
+ // [Life Cycle]:
157
+ // * load all workflows in db
158
+ // * add all hooks for enabled workflows
159
+ // * add hooks for create/update[enabled]/delete workflow to add/remove specific hooks
160
+ onAfterStart = async () => {
161
+ this.ready = true;
162
+ const collection = this.db.getCollection("workflows");
163
+ const workflows = await collection.repository.find({
164
+ filter: { enabled: true },
165
+ appends: ["stats", "versionStats"]
166
+ });
167
+ for (const workflow of workflows) {
168
+ if (!workflow.stats) {
169
+ workflow.stats = await workflow.createStats({ executed: 0 });
170
+ }
171
+ if (!workflow.versionStats) {
172
+ workflow.versionStats = await workflow.createVersionStats({ executed: 0 });
173
+ }
174
+ this.toggle(workflow, true, { silent: true });
175
+ }
176
+ this.checker = setInterval(() => {
177
+ this.getLogger("dispatcher").info(`(cycling) check for queueing executions`);
178
+ this.dispatch();
179
+ }, 3e5);
180
+ this.app.on("workflow:dispatch", () => {
181
+ this.app.logger.info("workflow:dispatch");
182
+ this.dispatch();
183
+ });
184
+ this.getLogger("dispatcher").info("(starting) check for queueing executions");
185
+ this.dispatch();
186
+ this.ready = true;
187
+ };
188
+ onBeforeStop = async () => {
189
+ this.app.logger.info(`stopping workflow plugin before app (${this.app.name}) shutdown...`);
190
+ for (const workflow of this.enabledCache.values()) {
191
+ this.toggle(workflow, false, { silent: true });
192
+ }
193
+ this.ready = false;
194
+ if (this.events.length) {
195
+ await this.prepare();
196
+ }
197
+ if (this.executing) {
198
+ await this.executing;
199
+ }
200
+ if (this.checker) {
201
+ clearInterval(this.checker);
202
+ }
203
+ };
106
204
  async handleSyncMessage(message) {
107
205
  if (message.type === "statusChange") {
108
206
  if (message.enabled) {
@@ -193,8 +291,18 @@ class PluginWorkflowServer extends import_server.Plugin {
193
291
  }
194
292
  async beforeLoad() {
195
293
  this.db.registerRepositories({
196
- WorkflowRepository: import_WorkflowRepository.default,
197
- WorkflowTasksRepository: import_WorkflowTasksRepository.default
294
+ WorkflowRepository: import_WorkflowRepository.default
295
+ });
296
+ const PluginRepo = this.db.getRepository("applicationPlugins");
297
+ const pluginRecord = await PluginRepo.findOne({
298
+ filter: { name: this.name }
299
+ });
300
+ this.snowflake = new import_nodejs_snowflake.Snowflake({
301
+ custom_epoch: pluginRecord == null ? void 0 : pluginRecord.createdAt.getTime()
302
+ });
303
+ this.app.eventQueue.subscribe(`${this.name}.pendingExecution`, {
304
+ idle: () => !this.executing && !this.pending.length && !this.events.length,
305
+ process: this.onQueueExecution
198
306
  });
199
307
  }
200
308
  /**
@@ -230,77 +338,22 @@ class PluginWorkflowServer extends import_server.Plugin {
230
338
  "flow_nodes:update",
231
339
  "flow_nodes:destroy",
232
340
  "flow_nodes:test",
233
- "jobs:get"
341
+ "jobs:get",
342
+ "workflowCategories:*"
234
343
  ]
235
344
  });
236
345
  this.app.acl.registerSnippet({
237
346
  name: "ui.workflows",
238
347
  actions: ["workflows:list"]
239
348
  });
240
- this.app.acl.allow("workflowTasks", "countMine", "loggedIn");
349
+ this.app.acl.allow("userWorkflowTasks", "listMine", "loggedIn");
241
350
  this.app.acl.allow("*", ["trigger"], "loggedIn");
242
- this.db.addMigrations({
243
- namespace: this.name,
244
- directory: import_path.default.resolve(__dirname, "migrations"),
245
- context: {
246
- plugin: this
247
- }
248
- });
249
351
  db.on("workflows.beforeSave", this.onBeforeSave);
250
- db.on("workflows.afterCreate", (model, { transaction }) => {
251
- if (model.enabled) {
252
- this.toggle(model, true, { transaction });
253
- }
254
- });
255
- db.on(
256
- "workflows.afterUpdate",
257
- (model, { transaction }) => this.toggle(model, model.enabled, { transaction })
258
- );
259
- db.on("workflows.afterDestroy", async (model, { transaction }) => {
260
- this.toggle(model, false, { transaction });
261
- const TaskRepo = this.db.getRepository("workflowTasks");
262
- await TaskRepo.destroy({
263
- filter: {
264
- workflowId: model.id
265
- },
266
- transaction
267
- });
268
- });
269
- this.app.on("afterStart", async () => {
270
- this.ready = true;
271
- const collection = db.getCollection("workflows");
272
- const workflows = await collection.repository.find({
273
- filter: { enabled: true }
274
- });
275
- workflows.forEach((workflow) => {
276
- this.toggle(workflow, true, { silent: true });
277
- });
278
- this.checker = setInterval(() => {
279
- this.getLogger("dispatcher").info(`(cycling) check for queueing executions`);
280
- this.dispatch();
281
- }, 3e5);
282
- this.app.on("workflow:dispatch", () => {
283
- this.app.logger.info("workflow:dispatch");
284
- this.dispatch();
285
- });
286
- this.getLogger("dispatcher").info("(starting) check for queueing executions");
287
- this.dispatch();
288
- });
289
- this.app.on("beforeStop", async () => {
290
- for (const workflow of this.enabledCache.values()) {
291
- this.toggle(workflow, false, { silent: true });
292
- }
293
- this.ready = false;
294
- if (this.events.length) {
295
- await this.prepare();
296
- }
297
- if (this.executing) {
298
- await this.executing;
299
- }
300
- if (this.checker) {
301
- clearInterval(this.checker);
302
- }
303
- });
352
+ db.on("workflows.afterCreate", this.onAfterCreate);
353
+ db.on("workflows.afterUpdate", this.onAfterUpdate);
354
+ db.on("workflows.afterDestroy", this.onAfterDestroy);
355
+ this.app.on("afterStart", this.onAfterStart);
356
+ this.app.on("beforeStop", this.onBeforeStop);
304
357
  }
305
358
  toggle(workflow, enable, { silent, transaction } = {}) {
306
359
  const type = workflow.get("type");
@@ -314,11 +367,14 @@ class PluginWorkflowServer extends import_server.Plugin {
314
367
  const prev = workflow.previous();
315
368
  if (prev.config) {
316
369
  trigger.off({ ...workflow.get(), ...prev });
370
+ this.getLogger(workflow.id).info(`toggle OFF workflow ${workflow.id} based on configuration before updated`);
317
371
  }
318
372
  trigger.on(workflow);
373
+ this.getLogger(workflow.id).info(`toggle ON workflow ${workflow.id}`);
319
374
  this.enabledCache.set(workflow.id, workflow);
320
375
  } else {
321
376
  trigger.off(workflow);
377
+ this.getLogger(workflow.id).info(`toggle OFF workflow ${workflow.id}`);
322
378
  this.enabledCache.delete(workflow.id);
323
379
  }
324
380
  if (!silent) {
@@ -470,21 +526,20 @@ class PluginWorkflowServer extends import_server.Plugin {
470
526
  throw err;
471
527
  }
472
528
  this.getLogger(workflow.id).info(`execution of workflow ${workflow.id} created as ${execution.id}`);
473
- await workflow.increment(["executed", "allExecuted"], { transaction });
529
+ if (!workflow.stats) {
530
+ workflow.stats = await workflow.getStats({ transaction });
531
+ }
532
+ await workflow.stats.increment("executed", { transaction });
474
533
  if (this.db.options.dialect !== "postgres") {
475
- await workflow.reload({ transaction });
534
+ await workflow.stats.reload({ transaction });
535
+ }
536
+ if (!workflow.versionStats) {
537
+ workflow.versionStats = await workflow.getVersionStats({ transaction });
538
+ }
539
+ await workflow.versionStats.increment("executed", { transaction });
540
+ if (this.db.options.dialect !== "postgres") {
541
+ await workflow.versionStats.reload({ transaction });
476
542
  }
477
- await workflow.constructor.update(
478
- {
479
- allExecuted: workflow.allExecuted
480
- },
481
- {
482
- where: {
483
- key: workflow.key
484
- },
485
- transaction
486
- }
487
- );
488
543
  if (!sameTransaction) {
489
544
  await transaction.commit();
490
545
  }
@@ -505,8 +560,14 @@ class PluginWorkflowServer extends import_server.Plugin {
505
560
  logger.info(`preparing execution for event`);
506
561
  try {
507
562
  const execution = await this.createExecution(...event);
508
- if ((execution == null ? void 0 : execution.status) === import_constants.EXECUTION_STATUS.QUEUEING && !this.executing && !this.pending.length) {
509
- this.pending.push([execution]);
563
+ if ((execution == null ? void 0 : execution.status) === import_constants.EXECUTION_STATUS.QUEUEING) {
564
+ if (!this.executing && !this.pending.length) {
565
+ logger.info(`local pending list is empty, adding execution (${execution.id}) to pending list`);
566
+ this.pending.push([execution]);
567
+ } else {
568
+ logger.info(`local pending list is not empty, sending execution (${execution.id}) to queue`);
569
+ this.app.eventQueue.publish(`${this.name}.pendingExecution`, { executionId: execution.id });
570
+ }
510
571
  }
511
572
  } catch (error) {
512
573
  logger.error(`failed to create execution:`, { error });
@@ -638,36 +699,38 @@ class PluginWorkflowServer extends import_server.Plugin {
638
699
  /**
639
700
  * @experimental
640
701
  */
641
- async toggleTaskStatus(task, done, { transaction }) {
702
+ async updateTasksStats(userId, type, stats = { pending: 0, all: 0 }, { transaction }) {
642
703
  const { db } = this.app;
643
- const repository = db.getRepository("workflowTasks");
644
- if (done) {
645
- await repository.destroy({
646
- filter: {
647
- type: task.type,
648
- key: `${task.key}`
704
+ const repository = db.getRepository("userWorkflowTasks");
705
+ let record = await repository.findOne({
706
+ filter: {
707
+ userId,
708
+ type
709
+ },
710
+ transaction
711
+ });
712
+ if (record) {
713
+ await record.update(
714
+ {
715
+ stats
649
716
  },
650
- transaction
651
- });
717
+ { transaction }
718
+ );
652
719
  } else {
653
- await repository.updateOrCreate({
654
- filterKeys: ["key", "type"],
655
- values: task,
720
+ record = await repository.create({
721
+ values: {
722
+ userId,
723
+ type,
724
+ stats
725
+ },
656
726
  transaction
657
727
  });
658
728
  }
659
- if (task.userId) {
660
- const counts = await repository.countAll({
661
- where: {
662
- userId: task.userId,
663
- workflowId: { [import_database.Op.ne]: null }
664
- },
665
- transaction
666
- }) || [];
729
+ if (userId) {
667
730
  this.app.emit("ws:sendToTag", {
668
731
  tagKey: "userId",
669
- tagValue: `${task.userId}`,
670
- message: { type: "workflow:tasks:updated", payload: counts }
732
+ tagValue: `${userId}`,
733
+ message: { type: "workflow:tasks:updated", payload: record.get() }
671
734
  });
672
735
  }
673
736
  }
@@ -44,16 +44,9 @@ export default class Processor {
44
44
  * @experimental
45
45
  */
46
46
  nodesMap: Map<number, FlowNodeModel>;
47
- /**
48
- * @experimental
49
- */
50
- jobsMap: Map<number, JobModel>;
51
- /**
52
- * @experimental
53
- */
54
- jobsMapByNodeKey: {
55
- [key: string]: any;
56
- };
47
+ private jobsMapByNodeKey;
48
+ private jobResultsMapByNodeKey;
49
+ private jobsToSave;
57
50
  /**
58
51
  * @experimental
59
52
  */
@@ -72,7 +65,7 @@ export default class Processor {
72
65
  /**
73
66
  * @experimental
74
67
  */
75
- saveJob(payload: JobModel | Record<string, any>): Promise<JobModel>;
68
+ saveJob(payload: JobModel | Record<string, any>): JobModel;
76
69
  /**
77
70
  * @experimental
78
71
  */
@@ -78,14 +78,9 @@ class Processor {
78
78
  * @experimental
79
79
  */
80
80
  nodesMap = /* @__PURE__ */ new Map();
81
- /**
82
- * @experimental
83
- */
84
- jobsMap = /* @__PURE__ */ new Map();
85
- /**
86
- * @experimental
87
- */
88
81
  jobsMapByNodeKey = {};
82
+ jobResultsMapByNodeKey = {};
83
+ jobsToSave = /* @__PURE__ */ new Map();
89
84
  /**
90
85
  * @experimental
91
86
  */
@@ -107,9 +102,9 @@ class Processor {
107
102
  }
108
103
  makeJobs(jobs) {
109
104
  jobs.forEach((job) => {
110
- this.jobsMap.set(job.id, job);
111
105
  const node = this.nodesMap.get(job.nodeId);
112
- this.jobsMapByNodeKey[node.key] = job.result;
106
+ this.jobsMapByNodeKey[node.key] = job;
107
+ this.jobResultsMapByNodeKey[node.key] = job.result;
113
108
  });
114
109
  }
115
110
  async prepare() {
@@ -123,11 +118,13 @@ class Processor {
123
118
  execution.workflow = plugin.enabledCache.get(execution.workflowId) || await execution.getWorkflow({ transaction });
124
119
  }
125
120
  const nodes = await execution.workflow.getNodes({ transaction });
121
+ execution.workflow.nodes = nodes;
126
122
  this.makeNodes(nodes);
127
123
  const jobs = await execution.getJobs({
128
124
  order: [["id", "ASC"]],
129
125
  transaction
130
126
  });
127
+ execution.jobs = jobs;
131
128
  this.makeJobs(jobs);
132
129
  }
133
130
  async start() {
@@ -179,11 +176,10 @@ class Processor {
179
176
  }
180
177
  }
181
178
  if (!(job instanceof import_database.Model)) {
182
- job.upstreamId = prevJob instanceof import_database.Model ? prevJob.get("id") : null;
183
179
  job.nodeId = node.id;
184
180
  job.nodeKey = node.key;
185
181
  }
186
- const savedJob = await this.saveJob(job);
182
+ const savedJob = this.saveJob(job);
187
183
  this.logger.info(
188
184
  `execution (${this.execution.id}) run instruction [${node.type}] for node (${node.id}) finished as status: ${savedJob.status}`
189
185
  );
@@ -230,6 +226,30 @@ class Processor {
230
226
  return this.exec(instruction.resume.bind(instruction), node, job);
231
227
  }
232
228
  async exit(s) {
229
+ if (this.jobsToSave.size) {
230
+ const newJobs = [];
231
+ for (const job of this.jobsToSave.values()) {
232
+ if (job.isNewRecord) {
233
+ newJobs.push(job);
234
+ } else {
235
+ await job.save({ transaction: this.mainTransaction });
236
+ }
237
+ }
238
+ if (newJobs.length) {
239
+ const JobsModel = this.options.plugin.db.getModel("jobs");
240
+ await JobsModel.bulkCreate(
241
+ newJobs.map((job) => job.toJSON()),
242
+ {
243
+ transaction: this.mainTransaction,
244
+ returning: false
245
+ }
246
+ );
247
+ for (const job of newJobs) {
248
+ job.isNewRecord = false;
249
+ }
250
+ }
251
+ this.jobsToSave.clear();
252
+ }
233
253
  if (typeof s === "number") {
234
254
  const status = this.constructor.StatusMap[s] ?? Math.sign(s);
235
255
  await this.execution.update({ status }, { transaction: this.mainTransaction });
@@ -240,32 +260,29 @@ class Processor {
240
260
  this.logger.info(`execution (${this.execution.id}) exiting with status ${this.execution.status}`);
241
261
  return null;
242
262
  }
243
- // TODO(optimize)
244
263
  /**
245
264
  * @experimental
246
265
  */
247
- async saveJob(payload) {
266
+ saveJob(payload) {
248
267
  const { database } = this.execution.constructor;
249
- const { mainTransaction: transaction } = this;
250
268
  const { model } = database.getCollection("jobs");
251
269
  let job;
252
270
  if (payload instanceof model) {
253
- job = await payload.save({ transaction });
254
- } else if (payload.id) {
255
- job = await model.findByPk(payload.id, { transaction });
256
- await job.update(payload, { transaction });
271
+ job = payload;
272
+ job.set("updatedAt", /* @__PURE__ */ new Date());
257
273
  } else {
258
- job = await model.create(
259
- {
260
- ...payload,
261
- executionId: this.execution.id
262
- },
263
- { transaction }
264
- );
274
+ job = model.build({
275
+ ...payload,
276
+ id: this.options.plugin.snowflake.getUniqueID().toString(),
277
+ createdAt: /* @__PURE__ */ new Date(),
278
+ updatedAt: /* @__PURE__ */ new Date(),
279
+ executionId: this.execution.id
280
+ });
265
281
  }
266
- this.jobsMap.set(job.id, job);
282
+ this.jobsToSave.set(job.id, job);
267
283
  this.lastSavedJob = job;
268
- this.jobsMapByNodeKey[job.nodeKey] = job.result;
284
+ this.jobsMapByNodeKey[job.nodeKey] = job;
285
+ this.jobResultsMapByNodeKey[job.nodeKey] = job.result;
269
286
  return job;
270
287
  }
271
288
  /**
@@ -319,31 +336,19 @@ class Processor {
319
336
  * @experimental
320
337
  */
321
338
  findBranchParentJob(job, node) {
322
- for (let j = job; j; j = this.jobsMap.get(j.upstreamId)) {
323
- if (j.nodeId === node.id) {
324
- return j;
325
- }
326
- }
327
- return null;
339
+ return this.jobsMapByNodeKey[node.key];
328
340
  }
329
341
  /**
330
342
  * @experimental
331
343
  */
332
344
  findBranchLastJob(node, job) {
333
- const allJobs = Array.from(this.jobsMap.values());
345
+ const allJobs = Object.values(this.jobsMapByNodeKey);
334
346
  const branchJobs = [];
335
347
  for (let n = this.findBranchEndNode(node); n && n !== node.upstream; n = n.upstream) {
336
348
  branchJobs.push(...allJobs.filter((item) => item.nodeId === n.id));
337
349
  }
338
- branchJobs.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
339
- for (let i = branchJobs.length - 1; i >= 0; i -= 1) {
340
- for (let j = branchJobs[i]; j && j.id !== job.id; j = this.jobsMap.get(j.upstreamId)) {
341
- if (j.upstreamId === job.id) {
342
- return branchJobs[i];
343
- }
344
- }
345
- }
346
- return null;
350
+ branchJobs.sort((a, b) => a.updatedAt.getTime() - b.updatedAt.getTime());
351
+ return branchJobs[branchJobs.length - 1] || null;
347
352
  }
348
353
  /**
349
354
  * @experimental
@@ -362,12 +367,12 @@ class Processor {
362
367
  for (let n = includeSelfScope ? node : this.findBranchParentNode(node); n; n = this.findBranchParentNode(n)) {
363
368
  const instruction = this.options.plugin.instructions.get(n.type);
364
369
  if (typeof (instruction == null ? void 0 : instruction.getScope) === "function") {
365
- $scopes[n.id] = $scopes[n.key] = instruction.getScope(n, this.jobsMapByNodeKey[n.key], this);
370
+ $scopes[n.id] = $scopes[n.key] = instruction.getScope(n, this.jobResultsMapByNodeKey[n.key], this);
366
371
  }
367
372
  }
368
373
  return {
369
374
  $context: this.execution.context,
370
- $jobsMapByNodeKey: this.jobsMapByNodeKey,
375
+ $jobsMapByNodeKey: this.jobResultsMapByNodeKey,
371
376
  $system: systemFns,
372
377
  $scopes,
373
378
  $env: this.options.plugin.app.environment.getVariables()
@@ -42,7 +42,7 @@ module.exports = __toCommonJS(actions_exports);
42
42
  var workflows = __toESM(require("./workflows"));
43
43
  var nodes = __toESM(require("./nodes"));
44
44
  var executions = __toESM(require("./executions"));
45
- var workflowTasks = __toESM(require("./workflowTasks"));
45
+ var userWorkflowTasks = __toESM(require("./userWorkflowTasks"));
46
46
  function make(name, mod) {
47
47
  return Object.keys(mod).reduce(
48
48
  (result, key) => ({
@@ -64,6 +64,6 @@ function actions_default({ app }) {
64
64
  test: nodes.test
65
65
  }),
66
66
  ...make("executions", executions),
67
- ...make("workflowTasks", workflowTasks)
67
+ ...make("userWorkflowTasks", userWorkflowTasks)
68
68
  });
69
69
  }