@nocobase/plugin-workflow 1.6.0-alpha.2 → 1.6.0-alpha.21

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 (50) hide show
  1. package/dist/client/4147131201cde30c.js +10 -0
  2. package/dist/client/4a2b78cd09ce676a.js +10 -0
  3. package/dist/client/FlowContext.d.ts +2 -0
  4. package/dist/client/WorkflowTasks.d.ts +17 -0
  5. package/dist/client/e73d2e9169520d3e.js +10 -0
  6. package/dist/client/index.d.ts +3 -0
  7. package/dist/client/index.js +1 -1
  8. package/dist/client/nodes/calculation.d.ts +3 -2
  9. package/dist/client/nodes/condition.d.ts +1 -0
  10. package/dist/client/nodes/create.d.ts +3 -2
  11. package/dist/client/nodes/destroy.d.ts +2 -0
  12. package/dist/client/nodes/end.d.ts +2 -0
  13. package/dist/client/nodes/index.d.ts +1 -0
  14. package/dist/client/nodes/output.d.ts +31 -0
  15. package/dist/client/nodes/query.d.ts +5 -4
  16. package/dist/client/nodes/update.d.ts +3 -2
  17. package/dist/client/triggers/schedule/ScheduleModes.d.ts +5 -2
  18. package/dist/client/triggers/schedule/index.d.ts +2 -0
  19. package/dist/client/variable.d.ts +17 -6
  20. package/dist/externalVersion.js +11 -11
  21. package/dist/locale/en-US.json +3 -1
  22. package/dist/locale/zh-CN.json +11 -7
  23. package/dist/node_modules/cron-parser/package.json +1 -1
  24. package/dist/node_modules/lru-cache/package.json +1 -1
  25. package/dist/server/Plugin.d.ts +9 -6
  26. package/dist/server/Plugin.js +116 -45
  27. package/dist/server/Processor.d.ts +5 -0
  28. package/dist/server/Processor.js +15 -6
  29. package/dist/server/actions/workflows.js +5 -2
  30. package/dist/server/collections/executions.js +9 -0
  31. package/dist/server/collections/flow_nodes.js +1 -0
  32. package/dist/server/collections/jobs.js +1 -0
  33. package/dist/server/collections/workflows.js +1 -0
  34. package/dist/server/instructions/CreateInstruction.js +1 -1
  35. package/dist/server/instructions/DestroyInstruction.js +1 -1
  36. package/dist/server/instructions/UpdateInstruction.js +1 -1
  37. package/dist/server/triggers/CollectionTrigger.d.ts +6 -6
  38. package/dist/server/triggers/CollectionTrigger.js +51 -31
  39. package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.d.ts +5 -1
  40. package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.js +53 -14
  41. package/dist/server/triggers/ScheduleTrigger/StaticScheduleTrigger.d.ts +1 -0
  42. package/dist/server/triggers/ScheduleTrigger/StaticScheduleTrigger.js +3 -0
  43. package/dist/server/triggers/ScheduleTrigger/index.d.ts +2 -2
  44. package/dist/server/triggers/ScheduleTrigger/index.js +21 -3
  45. package/dist/server/triggers/index.d.ts +4 -1
  46. package/package.json +3 -3
  47. package/dist/client/923.index.js +0 -10
  48. package/dist/client/929.index.js +0 -10
  49. package/dist/client/999.index.js +0 -10
  50. /package/dist/client/{383.index.js → 739d458621edf81f.js} +0 -0
@@ -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");
@@ -126,7 +127,7 @@ class PluginWorkflowServer extends import_server.Plugin {
126
127
  /**
127
128
  * @experimental
128
129
  */
129
- getLogger(workflowId) {
130
+ getLogger(workflowId = "dispatcher") {
130
131
  const now = /* @__PURE__ */ new Date();
131
132
  const date = `${now.getFullYear()}-${`0${now.getMonth() + 1}`.slice(-2)}-${`0${now.getDate()}`.slice(-2)}`;
132
133
  const key = `${date}-${workflowId}}`;
@@ -256,7 +257,6 @@ class PluginWorkflowServer extends import_server.Plugin {
256
257
  (model, { transaction }) => this.toggle(model, false, { transaction })
257
258
  );
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,14 @@ class PluginWorkflowServer extends import_server.Plugin {
266
266
  this.toggle(workflow, true, { silent: true });
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.app.on("workflow:dispatch", () => {
273
+ this.app.logger.info("workflow:dispatch");
274
+ this.dispatch();
275
+ });
276
+ this.getLogger("dispatcher").info("(starting) check for queueing executions");
271
277
  this.dispatch();
272
278
  });
273
279
  this.app.on("beforeStop", async () => {
@@ -323,10 +329,19 @@ class PluginWorkflowServer extends import_server.Plugin {
323
329
  logger.debug(`ignored event data:`, context);
324
330
  return;
325
331
  }
326
- if (!options.manually && !workflow.enabled) {
332
+ if (!options.force && !options.manually && !workflow.enabled) {
327
333
  logger.warn(`workflow ${workflow.id} is not enabled, event will be ignored`);
328
334
  return;
329
335
  }
336
+ const duplicated = this.events.find(([w, c, { eventKey }]) => {
337
+ if (eventKey && options.eventKey) {
338
+ return eventKey === options.eventKey;
339
+ }
340
+ });
341
+ if (duplicated) {
342
+ logger.warn(`event of workflow ${workflow.id} is duplicated (${options.eventKey}), event will be ignored`);
343
+ return;
344
+ }
330
345
  if (context == null) {
331
346
  logger.warn(`workflow ${workflow.id} event data context is null, event will be ignored`);
332
347
  return;
@@ -340,9 +355,10 @@ class PluginWorkflowServer extends import_server.Plugin {
340
355
  logger.info(`new event triggered, now events: ${this.events.length}`);
341
356
  logger.debug(`event data:`, { context });
342
357
  if (this.events.length > 1) {
358
+ logger.info(`new event is pending to be prepared after previous preparation is finished`);
343
359
  return;
344
360
  }
345
- setTimeout(this.prepare);
361
+ setImmediate(this.prepare);
346
362
  }
347
363
  async triggerSync(workflow, context, { deferred, ...options } = {}) {
348
364
  let execution;
@@ -367,33 +383,63 @@ class PluginWorkflowServer extends import_server.Plugin {
367
383
  `execution (${job.execution.id}) resuming from job (${job.id}) added to pending list`
368
384
  );
369
385
  this.pending.push([job.execution, job]);
386
+ if (this.executing) {
387
+ await this.executing;
388
+ }
370
389
  this.dispatch();
371
390
  }
372
391
  /**
373
392
  * Start a deferred execution
374
393
  * @experimental
375
394
  */
376
- start(execution) {
395
+ async start(execution) {
377
396
  if (execution.status !== import_constants.EXECUTION_STATUS.STARTED) {
378
397
  return;
379
398
  }
399
+ this.getLogger(execution.workflowId).info(`starting deferred execution (${execution.id})`);
380
400
  this.pending.push([execution]);
401
+ if (this.executing) {
402
+ await this.executing;
403
+ }
381
404
  this.dispatch();
382
405
  }
383
- createProcessor(execution, options = {}) {
384
- return new import_Processor.default(execution, { ...options, plugin: this });
406
+ async validateEvent(workflow, context, options) {
407
+ const trigger = this.triggers.get(workflow.type);
408
+ const triggerValid = await trigger.validateEvent(workflow, context, options);
409
+ if (!triggerValid) {
410
+ return false;
411
+ }
412
+ const { stack } = options;
413
+ let valid = true;
414
+ if ((stack == null ? void 0 : stack.length) > 0) {
415
+ const existed = await workflow.countExecutions({
416
+ where: {
417
+ id: stack
418
+ },
419
+ transaction: options.transaction
420
+ });
421
+ const limitCount = workflow.options.stackLimit || 1;
422
+ if (existed >= limitCount) {
423
+ this.getLogger(workflow.id).warn(
424
+ `workflow ${workflow.id} has already been triggered in stacks executions (${stack}), and max call coont is ${limitCount}, newly triggering will be skipped.`
425
+ );
426
+ valid = false;
427
+ }
428
+ }
429
+ return valid;
385
430
  }
386
431
  async createExecution(workflow, context, options) {
432
+ var _a;
387
433
  const { deferred } = options;
388
434
  const transaction = await this.useDataSourceTransaction("main", options.transaction, true);
389
435
  const sameTransaction = options.transaction === transaction;
390
- const trigger = this.triggers.get(workflow.type);
391
- const valid = await trigger.validateEvent(workflow, context, { ...options, transaction });
436
+ const valid = await this.validateEvent(workflow, context, { ...options, transaction });
392
437
  if (!valid) {
393
438
  if (!sameTransaction) {
394
439
  await transaction.commit();
395
440
  }
396
- return null;
441
+ (_a = options.onTriggerFail) == null ? void 0 : _a.call(options, workflow, context, options);
442
+ return Promise.reject(new Error("event is not valid"));
397
443
  }
398
444
  let execution;
399
445
  try {
@@ -402,6 +448,7 @@ class PluginWorkflowServer extends import_server.Plugin {
402
448
  context,
403
449
  key: workflow.key,
404
450
  eventKey: options.eventKey ?? (0, import_crypto.randomUUID)(),
451
+ stack: options.stack,
405
452
  status: deferred ? import_constants.EXECUTION_STATUS.STARTED : import_constants.EXECUTION_STATUS.QUEUEING
406
453
  },
407
454
  { transaction }
@@ -441,7 +488,7 @@ class PluginWorkflowServer extends import_server.Plugin {
441
488
  const event = this.events.shift();
442
489
  this.eventsCount = this.events.length;
443
490
  if (!event) {
444
- this.getLogger("dispatcher").warn(`events queue is empty, no need to prepare`);
491
+ this.getLogger("dispatcher").info(`events queue is empty, no need to prepare`);
445
492
  return;
446
493
  }
447
494
  const logger = this.getLogger(event[0].id);
@@ -451,12 +498,16 @@ class PluginWorkflowServer extends import_server.Plugin {
451
498
  if ((execution == null ? void 0 : execution.status) === import_constants.EXECUTION_STATUS.QUEUEING && !this.executing && !this.pending.length) {
452
499
  this.pending.push([execution]);
453
500
  }
454
- } catch (err) {
455
- logger.error(`failed to create execution: ${err.message}`, err);
501
+ } catch (error) {
502
+ logger.error(`failed to create execution:`, { error });
456
503
  }
457
504
  if (this.events.length) {
458
505
  await this.prepare();
459
506
  } else {
507
+ this.getLogger("dispatcher").info("no more events need to be prepared, dispatching...");
508
+ if (this.executing) {
509
+ await this.executing;
510
+ }
460
511
  this.dispatch();
461
512
  }
462
513
  };
@@ -474,58 +525,78 @@ class PluginWorkflowServer extends import_server.Plugin {
474
525
  }
475
526
  this.executing = (async () => {
476
527
  let next = null;
477
- try {
478
- if (this.pending.length) {
479
- next = this.pending.shift();
480
- this.getLogger(next[0].workflowId).info(`pending execution (${next[0].id}) ready to process`);
481
- } else {
482
- const execution = await this.db.getRepository("executions").findOne({
483
- filter: {
484
- status: import_constants.EXECUTION_STATUS.QUEUEING,
485
- "workflow.enabled": true,
486
- "workflow.id": {
487
- [import_database.Op.not]: null
488
- }
528
+ if (this.pending.length) {
529
+ next = this.pending.shift();
530
+ this.getLogger(next[0].workflowId).info(`pending execution (${next[0].id}) ready to process`);
531
+ } else {
532
+ try {
533
+ await this.db.sequelize.transaction(
534
+ {
535
+ isolationLevel: this.db.options.dialect === "sqlite" ? [][0] : import_sequelize.Transaction.ISOLATION_LEVELS.REPEATABLE_READ
489
536
  },
490
- appends: ["workflow"],
491
- sort: "id"
492
- });
493
- if (execution) {
494
- this.getLogger(execution.workflowId).info(`execution (${execution.id}) fetched from db`);
495
- next = [execution];
496
- }
497
- }
498
- if (next) {
499
- await this.process(...next);
500
- }
501
- } finally {
502
- this.executing = null;
503
- if (next) {
504
- this.dispatch();
537
+ async (transaction) => {
538
+ const execution = await this.db.getRepository("executions").findOne({
539
+ filter: {
540
+ status: import_constants.EXECUTION_STATUS.QUEUEING,
541
+ "workflow.enabled": true
542
+ },
543
+ sort: "id",
544
+ transaction
545
+ });
546
+ if (execution) {
547
+ this.getLogger(execution.workflowId).info(`execution (${execution.id}) fetched from db`);
548
+ await execution.update(
549
+ {
550
+ status: import_constants.EXECUTION_STATUS.STARTED
551
+ },
552
+ { transaction }
553
+ );
554
+ execution.workflow = this.enabledCache.get(execution.workflowId);
555
+ next = [execution];
556
+ } else {
557
+ this.getLogger("dispatcher").info(`no execution in db queued to process`);
558
+ }
559
+ }
560
+ );
561
+ } catch (error) {
562
+ this.getLogger("dispatcher").error(`fetching execution from db failed: ${error.message}`, { error });
505
563
  }
506
564
  }
565
+ if (next) {
566
+ await this.process(...next);
567
+ }
568
+ this.executing = null;
569
+ if (next || this.pending.length) {
570
+ this.getLogger("dispatcher").info(`last process finished, will do another dispatch`);
571
+ this.dispatch();
572
+ }
507
573
  })();
508
574
  }
575
+ createProcessor(execution, options = {}) {
576
+ return new import_Processor.default(execution, { ...options, plugin: this });
577
+ }
509
578
  async process(execution, job, options = {}) {
510
579
  var _a, _b;
580
+ const logger = this.getLogger(execution.workflowId);
511
581
  if (execution.status === import_constants.EXECUTION_STATUS.QUEUEING) {
512
- await execution.update({ status: import_constants.EXECUTION_STATUS.STARTED }, { transaction: options.transaction });
582
+ const transaction = await this.useDataSourceTransaction("main", options.transaction);
583
+ await execution.update({ status: import_constants.EXECUTION_STATUS.STARTED }, { transaction });
584
+ logger.info(`queueing execution (${execution.id}) from pending list updated to started`);
513
585
  }
514
- const logger = this.getLogger(execution.workflowId);
515
586
  const processor = this.createProcessor(execution, options);
516
587
  logger.info(`execution (${execution.id}) ${job ? "resuming" : "starting"}...`);
517
588
  try {
518
589
  await (job ? processor.resume(job) : processor.start());
519
590
  logger.info(`execution (${execution.id}) finished with status: ${execution.status}`, { execution });
520
591
  if (execution.status && ((_b = (_a = execution.workflow.options) == null ? void 0 : _a.deleteExecutionOnStatus) == null ? void 0 : _b.includes(execution.status))) {
521
- await execution.destroy();
592
+ await execution.destroy({ transaction: processor.mainTransaction });
522
593
  }
523
594
  } catch (err) {
524
595
  logger.error(`execution (${execution.id}) error: ${err.message}`, err);
525
596
  }
526
597
  return processor;
527
598
  }
528
- async execute(workflow, context, options = {}) {
599
+ async execute(workflow, values, options = {}) {
529
600
  const trigger = this.triggers.get(workflow.type);
530
601
  if (!trigger) {
531
602
  throw new Error(`trigger type "${workflow.type}" of workflow ${workflow.id} is not registered`);
@@ -533,7 +604,7 @@ class PluginWorkflowServer extends import_server.Plugin {
533
604
  if (!trigger.execute) {
534
605
  throw new Error(`"execute" method of trigger ${workflow.type} is not implemented`);
535
606
  }
536
- return trigger.execute(workflow, context, options);
607
+ return trigger.execute(workflow, values, options);
537
608
  }
538
609
  /**
539
610
  * @experimental
@@ -32,6 +32,10 @@ export default class Processor {
32
32
  * @experimental
33
33
  */
34
34
  transaction: Transaction;
35
+ /**
36
+ * @experimental
37
+ */
38
+ mainTransaction: Transaction;
35
39
  /**
36
40
  * @experimental
37
41
  */
@@ -105,6 +109,7 @@ export default class Processor {
105
109
  };
106
110
  $system: {};
107
111
  $scopes: {};
112
+ $env: {};
108
113
  };
109
114
  /**
110
115
  * @experimental
@@ -66,6 +66,10 @@ class Processor {
66
66
  * @experimental
67
67
  */
68
68
  transaction;
69
+ /**
70
+ * @experimental
71
+ */
72
+ mainTransaction;
69
73
  /**
70
74
  * @experimental
71
75
  */
@@ -111,11 +115,12 @@ class Processor {
111
115
  async prepare() {
112
116
  const {
113
117
  execution,
114
- transaction,
115
118
  options: { plugin }
116
119
  } = this;
120
+ this.mainTransaction = plugin.useDataSourceTransaction("main", this.transaction);
121
+ const transaction = this.mainTransaction;
117
122
  if (!execution.workflow) {
118
- execution.workflow = plugin.enabledCache.get(execution.workflowId);
123
+ execution.workflow = plugin.enabledCache.get(execution.workflowId) || await execution.getWorkflow({ transaction });
119
124
  }
120
125
  const nodes = await execution.workflow.getNodes({ transaction });
121
126
  this.makeNodes(nodes);
@@ -154,7 +159,7 @@ class Processor {
154
159
  this.logger.debug(`config of node`, { data: node.config });
155
160
  job = await instruction(node, prevJob, this);
156
161
  if (!job) {
157
- return null;
162
+ return this.exit();
158
163
  }
159
164
  } catch (err) {
160
165
  this.logger.error(
@@ -227,7 +232,10 @@ class Processor {
227
232
  async exit(s) {
228
233
  if (typeof s === "number") {
229
234
  const status = this.constructor.StatusMap[s] ?? Math.sign(s);
230
- await this.execution.update({ status }, { transaction: this.transaction });
235
+ await this.execution.update({ status }, { transaction: this.mainTransaction });
236
+ }
237
+ if (this.mainTransaction && this.mainTransaction !== this.transaction) {
238
+ await this.mainTransaction.commit();
231
239
  }
232
240
  this.logger.info(`execution (${this.execution.id}) exiting with status ${this.execution.status}`);
233
241
  return null;
@@ -238,7 +246,7 @@ class Processor {
238
246
  */
239
247
  async saveJob(payload) {
240
248
  const { database } = this.execution.constructor;
241
- const { transaction } = this;
249
+ const { mainTransaction: transaction } = this;
242
250
  const { model } = database.getCollection("jobs");
243
251
  let job;
244
252
  if (payload instanceof model) {
@@ -361,7 +369,8 @@ class Processor {
361
369
  $context: this.execution.context,
362
370
  $jobsMapByNodeKey: this.jobsMapByNodeKey,
363
371
  $system: systemFns,
364
- $scopes
372
+ $scopes,
373
+ $env: this.options.plugin.app.environment.getVariables()
365
374
  };
366
375
  }
367
376
  /**
@@ -121,7 +121,10 @@ async function trigger(context, next) {
121
121
  }
122
122
  async function execute(context, next) {
123
123
  const plugin = context.app.pm.get(import_Plugin.default);
124
- const { filterByTk, autoRevision } = context.action.params;
124
+ const { filterByTk, values, autoRevision } = context.action.params;
125
+ if (!values) {
126
+ return context.throw(400, "values is required");
127
+ }
125
128
  if (!filterByTk) {
126
129
  return context.throw(400, "filterByTk is required");
127
130
  }
@@ -137,7 +140,7 @@ async function execute(context, next) {
137
140
  const { executed } = workflow;
138
141
  let processor;
139
142
  try {
140
- processor = await plugin.execute(workflow, context, { manually: true });
143
+ processor = await plugin.execute(workflow, values, { manually: true });
141
144
  if (!processor) {
142
145
  return context.throw(400, "workflow not triggered");
143
146
  }
@@ -33,6 +33,7 @@ var executions_default = {
33
33
  dumpRules: {
34
34
  group: "log"
35
35
  },
36
+ migrationRules: ["schema-only"],
36
37
  name: "executions",
37
38
  shared: true,
38
39
  fields: [
@@ -61,6 +62,14 @@ var executions_default = {
61
62
  {
62
63
  type: "integer",
63
64
  name: "status"
65
+ },
66
+ {
67
+ type: "json",
68
+ name: "stack"
69
+ },
70
+ {
71
+ type: "json",
72
+ name: "output"
64
73
  }
65
74
  ]
66
75
  };
@@ -31,6 +31,7 @@ __export(flow_nodes_exports, {
31
31
  module.exports = __toCommonJS(flow_nodes_exports);
32
32
  var flow_nodes_default = {
33
33
  dumpRules: "required",
34
+ migrationRules: ["overwrite", "schema-only"],
34
35
  name: "flow_nodes",
35
36
  shared: true,
36
37
  fields: [
@@ -33,6 +33,7 @@ var jobs_default = {
33
33
  dumpRules: {
34
34
  group: "log"
35
35
  },
36
+ migrationRules: ["schema-only"],
36
37
  name: "jobs",
37
38
  shared: true,
38
39
  fields: [
@@ -32,6 +32,7 @@ module.exports = __toCommonJS(workflows_exports);
32
32
  function workflows_default() {
33
33
  return {
34
34
  dumpRules: "required",
35
+ migrationRules: ["overwrite", "schema-only"],
35
36
  name: "workflows",
36
37
  shared: true,
37
38
  repository: "WorkflowRepository",
@@ -44,7 +44,7 @@ class CreateInstruction extends import__.Instruction {
44
44
  const created = await repository.create({
45
45
  ...options,
46
46
  context: {
47
- stack: Array.from(new Set((processor.execution.context.stack ?? []).concat(processor.execution.id)))
47
+ stack: Array.from(new Set((processor.execution.stack ?? []).concat(processor.execution.id)))
48
48
  },
49
49
  transaction
50
50
  });
@@ -42,7 +42,7 @@ class DestroyInstruction extends import__.Instruction {
42
42
  const result = await repository.destroy({
43
43
  ...options,
44
44
  context: {
45
- stack: Array.from(new Set((processor.execution.context.stack ?? []).concat(processor.execution.id)))
45
+ stack: Array.from(new Set((processor.execution.stack ?? []).concat(processor.execution.id)))
46
46
  },
47
47
  transaction: this.workflow.useDataSourceTransaction(dataSourceName, processor.transaction)
48
48
  });
@@ -42,7 +42,7 @@ class UpdateInstruction extends import__.Instruction {
42
42
  const result = await repository.update({
43
43
  ...options,
44
44
  context: {
45
- stack: Array.from(new Set((processor.execution.context.stack ?? []).concat(processor.execution.id)))
45
+ stack: Array.from(new Set((processor.execution.stack ?? []).concat(processor.execution.id)))
46
46
  },
47
47
  transaction: this.workflow.useDataSourceTransaction(dataSourceName, processor.transaction)
48
48
  });
@@ -6,11 +6,10 @@
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 { Model, Transactionable } from '@nocobase/database';
9
+ import { Model } from '@nocobase/database';
10
10
  import Trigger from '.';
11
11
  import type { WorkflowModel } from '../types';
12
12
  import type { EventOptions } from '../Plugin';
13
- import { Context } from '@nocobase/actions';
14
13
  export interface CollectionChangeTriggerConfig {
15
14
  collection: string;
16
15
  mode: number;
@@ -19,12 +18,13 @@ export interface CollectionChangeTriggerConfig {
19
18
  export default class CollectionTrigger extends Trigger {
20
19
  events: Map<any, any>;
21
20
  private static handler;
22
- prepare(workflow: WorkflowModel, data: Model | Record<string, any>, options: any): Promise<{
21
+ prepare(workflow: WorkflowModel, data: Model | Record<string, any> | string | number, options: any): Promise<{
23
22
  data: any;
24
- stack: any;
25
23
  }>;
26
24
  on(workflow: WorkflowModel): void;
27
25
  off(workflow: WorkflowModel): void;
28
- validateEvent(workflow: WorkflowModel, context: any, options: Transactionable): Promise<boolean>;
29
- execute(workflow: WorkflowModel, context: Context, options: EventOptions): Promise<void | import("..").Processor>;
26
+ execute(workflow: WorkflowModel, values: any, options: EventOptions): Promise<void | import("..").Processor>;
27
+ validateContext(values: any): {
28
+ data: string;
29
+ };
30
30
  }
@@ -74,17 +74,19 @@ class CollectionTrigger extends import__.default {
74
74
  if (!ctx) {
75
75
  return;
76
76
  }
77
+ const { stack } = options.context ?? {};
77
78
  if (workflow.sync) {
78
79
  await this.workflow.trigger(workflow, ctx, {
79
- transaction
80
+ transaction,
81
+ stack
80
82
  });
81
83
  } else {
82
84
  if (transaction) {
83
85
  transaction.afterCommit(() => {
84
- this.workflow.trigger(workflow, ctx);
86
+ this.workflow.trigger(workflow, ctx, { stack });
85
87
  });
86
88
  } else {
87
- this.workflow.trigger(workflow, ctx);
89
+ this.workflow.trigger(workflow, ctx, { stack });
88
90
  }
89
91
  }
90
92
  }
@@ -95,13 +97,24 @@ class CollectionTrigger extends import__.default {
95
97
  const collection = collectionManager.getCollection(collectionName);
96
98
  const { transaction, context } = options;
97
99
  const { repository, filterTargetKey } = collection;
98
- if (data instanceof import_database.Model && changed && changed.length && changed.filter((name) => {
100
+ let target = data;
101
+ let filterByTk;
102
+ let loadNeeded = false;
103
+ if (target && typeof target === "object") {
104
+ filterByTk = Array.isArray(filterTargetKey) ? (0, import_lodash.pick)(
105
+ target,
106
+ filterTargetKey.sort((a, b) => a.localeCompare(b))
107
+ ) : target[filterTargetKey];
108
+ } else {
109
+ filterByTk = target;
110
+ loadNeeded = true;
111
+ }
112
+ if (target instanceof import_database.Model && changed && changed.length && changed.filter((name) => {
99
113
  const field = collection.getField(name);
100
114
  return field && !["linkTo", "hasOne", "hasMany", "belongsToMany"].includes(field.options.type);
101
- }).every((name) => !data.changedWithAssociations(getFieldRawName(collection, name)))) {
115
+ }).every((name) => !target.changedWithAssociations(getFieldRawName(collection, name)))) {
102
116
  return null;
103
117
  }
104
- const filterByTk = Array.isArray(filterTargetKey) ? (0, import_lodash.pick)(data, filterTargetKey) : { [filterTargetKey]: data[filterTargetKey] };
105
118
  if ((0, import_utils.isValidFilter)(condition) && !(mode & MODE_BITMAP.DESTROY)) {
106
119
  const count = await repository.count({
107
120
  filterByTk,
@@ -113,22 +126,20 @@ class CollectionTrigger extends import__.default {
113
126
  return null;
114
127
  }
115
128
  }
116
- let result = data;
117
- if ((appends == null ? void 0 : appends.length) && !(mode & MODE_BITMAP.DESTROY)) {
129
+ if (loadNeeded || (appends == null ? void 0 : appends.length) && !(mode & MODE_BITMAP.DESTROY)) {
118
130
  const includeFields = appends.reduce((set, field) => {
119
131
  set.add(field.split(".")[0]);
120
132
  set.add(field);
121
133
  return set;
122
134
  }, /* @__PURE__ */ new Set());
123
- result = await repository.findOne({
135
+ target = await repository.findOne({
124
136
  filterByTk,
125
137
  appends: Array.from(includeFields),
126
138
  transaction
127
139
  });
128
140
  }
129
141
  return {
130
- data: (0, import_utils2.toJSON)(result),
131
- stack: context == null ? void 0 : context.stack
142
+ data: (0, import_utils2.toJSON)(target)
132
143
  };
133
144
  }
134
145
  on(workflow) {
@@ -182,26 +193,27 @@ class CollectionTrigger extends import__.default {
182
193
  }
183
194
  }
184
195
  }
185
- async validateEvent(workflow, context, options) {
186
- if (context.stack) {
187
- const existed = await workflow.countExecutions({
188
- where: {
189
- id: context.stack
190
- },
191
- transaction: options.transaction
192
- });
193
- if (existed) {
194
- this.workflow.getLogger(workflow.id).warn(
195
- `workflow ${workflow.id} has already been triggered in stack executions (${context.stack}), and newly triggering will be skipped.`
196
- );
197
- return false;
198
- }
199
- }
200
- return true;
201
- }
202
- async execute(workflow, context, options) {
203
- var _a;
204
- const ctx = await this.prepare(workflow, (_a = context.action.params.values) == null ? void 0 : _a.data, options);
196
+ // async validateEvent(workflow: WorkflowModel, context: any, options: Transactionable): Promise<boolean> {
197
+ // if (context.stack) {
198
+ // const existed = await workflow.countExecutions({
199
+ // where: {
200
+ // id: context.stack,
201
+ // },
202
+ // transaction: options.transaction,
203
+ // });
204
+ // if (existed) {
205
+ // this.workflow
206
+ // .getLogger(workflow.id)
207
+ // .warn(
208
+ // `workflow ${workflow.id} has already been triggered in stack executions (${context.stack}), and newly triggering will be skipped.`,
209
+ // );
210
+ // return false;
211
+ // }
212
+ // }
213
+ // return true;
214
+ // }
215
+ async execute(workflow, values, options) {
216
+ const ctx = await this.prepare(workflow, values == null ? void 0 : values.data, options);
205
217
  const [dataSourceName] = (0, import_data_source_manager.parseCollectionName)(workflow.config.collection);
206
218
  const { transaction } = options;
207
219
  return this.workflow.trigger(workflow, ctx, {
@@ -209,4 +221,12 @@ class CollectionTrigger extends import__.default {
209
221
  transaction: this.workflow.useDataSourceTransaction(dataSourceName, transaction)
210
222
  });
211
223
  }
224
+ validateContext(values) {
225
+ if (!values.data) {
226
+ return {
227
+ data: "Data is required"
228
+ };
229
+ }
230
+ return null;
231
+ }
212
232
  }
@@ -30,10 +30,14 @@ export default class DateFieldScheduleTrigger {
30
30
  constructor(workflow: Plugin);
31
31
  reload(): Promise<void>;
32
32
  inspect(workflows: WorkflowModel[]): void;
33
- loadRecordsToSchedule({ config: { collection, limit, startsOn, repeat, endsOn }, allExecuted }: WorkflowModel, currentDate: Date): Promise<import("@nocobase/database").Model<any, any>[]>;
33
+ loadRecordsToSchedule({ id, config: { collection, limit, startsOn, repeat, endsOn }, allExecuted }: WorkflowModel, currentDate: Date): Promise<import("@nocobase/database").Model<any, any>[]>;
34
34
  getRecordNextTime(workflow: WorkflowModel, record: any, nextSecond?: boolean): any;
35
35
  schedule(workflow: WorkflowModel, record: any, nextTime: any, toggle?: boolean, options?: {}): Promise<void>;
36
36
  trigger(workflow: WorkflowModel, record: any, nextTime: any, { transaction }?: Transactionable): Promise<void>;
37
37
  on(workflow: WorkflowModel): void;
38
38
  off(workflow: WorkflowModel): void;
39
+ execute(workflow: any, values: any, options: any): Promise<void | import("../..").Processor>;
40
+ validateContext(values: any): {
41
+ data: string;
42
+ };
39
43
  }