@nocobase/plugin-workflow 1.6.0-alpha.3 → 1.6.0-alpha.30

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 (51) hide show
  1. package/dist/client/4d75ef32f02d7285.js +10 -0
  2. package/dist/client/56ce448358002e64.js +10 -0
  3. package/dist/client/58bb427e05b600de.js +10 -0
  4. package/dist/client/FlowContext.d.ts +2 -0
  5. package/dist/client/WorkflowTasks.d.ts +17 -0
  6. package/dist/client/index.d.ts +20 -7
  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/settings/{customizeSubmitToWorkflowActionSettings.d.ts → BindWorkflowConfig.d.ts} +2 -2
  18. package/dist/client/triggers/schedule/ScheduleModes.d.ts +5 -2
  19. package/dist/client/triggers/schedule/index.d.ts +2 -0
  20. package/dist/client/variable.d.ts +17 -6
  21. package/dist/externalVersion.js +14 -14
  22. package/dist/locale/en-US.json +3 -1
  23. package/dist/locale/zh-CN.json +11 -7
  24. package/dist/node_modules/cron-parser/package.json +1 -1
  25. package/dist/node_modules/lru-cache/package.json +1 -1
  26. package/dist/server/Plugin.d.ts +9 -6
  27. package/dist/server/Plugin.js +116 -45
  28. package/dist/server/Processor.d.ts +1 -0
  29. package/dist/server/Processor.js +6 -5
  30. package/dist/server/actions/workflows.d.ts +0 -1
  31. package/dist/server/actions/workflows.js +5 -7
  32. package/dist/server/collections/executions.js +9 -0
  33. package/dist/server/collections/flow_nodes.js +1 -0
  34. package/dist/server/collections/jobs.js +1 -0
  35. package/dist/server/collections/workflows.js +1 -0
  36. package/dist/server/instructions/CreateInstruction.js +1 -1
  37. package/dist/server/instructions/DestroyInstruction.js +1 -1
  38. package/dist/server/instructions/UpdateInstruction.js +1 -1
  39. package/dist/server/triggers/CollectionTrigger.d.ts +6 -6
  40. package/dist/server/triggers/CollectionTrigger.js +54 -31
  41. package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.d.ts +5 -1
  42. package/dist/server/triggers/ScheduleTrigger/DateFieldScheduleTrigger.js +53 -14
  43. package/dist/server/triggers/ScheduleTrigger/StaticScheduleTrigger.d.ts +1 -0
  44. package/dist/server/triggers/ScheduleTrigger/StaticScheduleTrigger.js +3 -0
  45. package/dist/server/triggers/ScheduleTrigger/index.d.ts +2 -2
  46. package/dist/server/triggers/ScheduleTrigger/index.js +21 -3
  47. package/dist/server/triggers/index.d.ts +4 -1
  48. package/package.json +3 -3
  49. package/dist/client/5ed8ff0f70ed5911.js +0 -10
  50. package/dist/client/92877729dbcede8f.js +0 -10
  51. package/dist/client/e7b9d67c6a964bec.js +0 -10
@@ -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}}`;
@@ -226,7 +227,8 @@ class PluginWorkflowServer extends import_server.Plugin {
226
227
  "executions:destroy",
227
228
  "flow_nodes:update",
228
229
  "flow_nodes:destroy",
229
- "flow_nodes:test"
230
+ "flow_nodes:test",
231
+ "jobs:get"
230
232
  ]
231
233
  });
232
234
  this.app.acl.registerSnippet({
@@ -256,7 +258,6 @@ class PluginWorkflowServer extends import_server.Plugin {
256
258
  (model, { transaction }) => this.toggle(model, false, { transaction })
257
259
  );
258
260
  this.app.on("afterStart", async () => {
259
- this.app.setMaintainingMessage("check for not started executions");
260
261
  this.ready = true;
261
262
  const collection = db.getCollection("workflows");
262
263
  const workflows = await collection.repository.find({
@@ -266,8 +267,14 @@ class PluginWorkflowServer extends import_server.Plugin {
266
267
  this.toggle(workflow, true, { silent: true });
267
268
  });
268
269
  this.checker = setInterval(() => {
270
+ this.getLogger("dispatcher").info(`(cycling) check for queueing executions`);
269
271
  this.dispatch();
270
272
  }, 3e5);
273
+ this.app.on("workflow:dispatch", () => {
274
+ this.app.logger.info("workflow:dispatch");
275
+ this.dispatch();
276
+ });
277
+ this.getLogger("dispatcher").info("(starting) check for queueing executions");
271
278
  this.dispatch();
272
279
  });
273
280
  this.app.on("beforeStop", async () => {
@@ -323,10 +330,19 @@ class PluginWorkflowServer extends import_server.Plugin {
323
330
  logger.debug(`ignored event data:`, context);
324
331
  return;
325
332
  }
326
- if (!options.manually && !workflow.enabled) {
333
+ if (!options.force && !options.manually && !workflow.enabled) {
327
334
  logger.warn(`workflow ${workflow.id} is not enabled, event will be ignored`);
328
335
  return;
329
336
  }
337
+ const duplicated = this.events.find(([w, c, { eventKey }]) => {
338
+ if (eventKey && options.eventKey) {
339
+ return eventKey === options.eventKey;
340
+ }
341
+ });
342
+ if (duplicated) {
343
+ logger.warn(`event of workflow ${workflow.id} is duplicated (${options.eventKey}), event will be ignored`);
344
+ return;
345
+ }
330
346
  if (context == null) {
331
347
  logger.warn(`workflow ${workflow.id} event data context is null, event will be ignored`);
332
348
  return;
@@ -340,9 +356,10 @@ class PluginWorkflowServer extends import_server.Plugin {
340
356
  logger.info(`new event triggered, now events: ${this.events.length}`);
341
357
  logger.debug(`event data:`, { context });
342
358
  if (this.events.length > 1) {
359
+ logger.info(`new event is pending to be prepared after previous preparation is finished`);
343
360
  return;
344
361
  }
345
- setTimeout(this.prepare);
362
+ setImmediate(this.prepare);
346
363
  }
347
364
  async triggerSync(workflow, context, { deferred, ...options } = {}) {
348
365
  let execution;
@@ -367,33 +384,63 @@ class PluginWorkflowServer extends import_server.Plugin {
367
384
  `execution (${job.execution.id}) resuming from job (${job.id}) added to pending list`
368
385
  );
369
386
  this.pending.push([job.execution, job]);
387
+ if (this.executing) {
388
+ await this.executing;
389
+ }
370
390
  this.dispatch();
371
391
  }
372
392
  /**
373
393
  * Start a deferred execution
374
394
  * @experimental
375
395
  */
376
- start(execution) {
396
+ async start(execution) {
377
397
  if (execution.status !== import_constants.EXECUTION_STATUS.STARTED) {
378
398
  return;
379
399
  }
400
+ this.getLogger(execution.workflowId).info(`starting deferred execution (${execution.id})`);
380
401
  this.pending.push([execution]);
402
+ if (this.executing) {
403
+ await this.executing;
404
+ }
381
405
  this.dispatch();
382
406
  }
383
- createProcessor(execution, options = {}) {
384
- return new import_Processor.default(execution, { ...options, plugin: this });
407
+ async validateEvent(workflow, context, options) {
408
+ const trigger = this.triggers.get(workflow.type);
409
+ const triggerValid = await trigger.validateEvent(workflow, context, options);
410
+ if (!triggerValid) {
411
+ return false;
412
+ }
413
+ const { stack } = options;
414
+ let valid = true;
415
+ if ((stack == null ? void 0 : stack.length) > 0) {
416
+ const existed = await workflow.countExecutions({
417
+ where: {
418
+ id: stack
419
+ },
420
+ transaction: options.transaction
421
+ });
422
+ const limitCount = workflow.options.stackLimit || 1;
423
+ if (existed >= limitCount) {
424
+ this.getLogger(workflow.id).warn(
425
+ `workflow ${workflow.id} has already been triggered in stacks executions (${stack}), and max call coont is ${limitCount}, newly triggering will be skipped.`
426
+ );
427
+ valid = false;
428
+ }
429
+ }
430
+ return valid;
385
431
  }
386
432
  async createExecution(workflow, context, options) {
433
+ var _a;
387
434
  const { deferred } = options;
388
435
  const transaction = await this.useDataSourceTransaction("main", options.transaction, true);
389
436
  const sameTransaction = options.transaction === transaction;
390
- const trigger = this.triggers.get(workflow.type);
391
- const valid = await trigger.validateEvent(workflow, context, { ...options, transaction });
437
+ const valid = await this.validateEvent(workflow, context, { ...options, transaction });
392
438
  if (!valid) {
393
439
  if (!sameTransaction) {
394
440
  await transaction.commit();
395
441
  }
396
- return null;
442
+ (_a = options.onTriggerFail) == null ? void 0 : _a.call(options, workflow, context, options);
443
+ return Promise.reject(new Error("event is not valid"));
397
444
  }
398
445
  let execution;
399
446
  try {
@@ -402,6 +449,7 @@ class PluginWorkflowServer extends import_server.Plugin {
402
449
  context,
403
450
  key: workflow.key,
404
451
  eventKey: options.eventKey ?? (0, import_crypto.randomUUID)(),
452
+ stack: options.stack,
405
453
  status: deferred ? import_constants.EXECUTION_STATUS.STARTED : import_constants.EXECUTION_STATUS.QUEUEING
406
454
  },
407
455
  { transaction }
@@ -441,7 +489,7 @@ class PluginWorkflowServer extends import_server.Plugin {
441
489
  const event = this.events.shift();
442
490
  this.eventsCount = this.events.length;
443
491
  if (!event) {
444
- this.getLogger("dispatcher").warn(`events queue is empty, no need to prepare`);
492
+ this.getLogger("dispatcher").info(`events queue is empty, no need to prepare`);
445
493
  return;
446
494
  }
447
495
  const logger = this.getLogger(event[0].id);
@@ -451,12 +499,16 @@ class PluginWorkflowServer extends import_server.Plugin {
451
499
  if ((execution == null ? void 0 : execution.status) === import_constants.EXECUTION_STATUS.QUEUEING && !this.executing && !this.pending.length) {
452
500
  this.pending.push([execution]);
453
501
  }
454
- } catch (err) {
455
- logger.error(`failed to create execution: ${err.message}`, err);
502
+ } catch (error) {
503
+ logger.error(`failed to create execution:`, { error });
456
504
  }
457
505
  if (this.events.length) {
458
506
  await this.prepare();
459
507
  } else {
508
+ this.getLogger("dispatcher").info("no more events need to be prepared, dispatching...");
509
+ if (this.executing) {
510
+ await this.executing;
511
+ }
460
512
  this.dispatch();
461
513
  }
462
514
  };
@@ -474,59 +526,78 @@ class PluginWorkflowServer extends import_server.Plugin {
474
526
  }
475
527
  this.executing = (async () => {
476
528
  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
- }
529
+ if (this.pending.length) {
530
+ next = this.pending.shift();
531
+ this.getLogger(next[0].workflowId).info(`pending execution (${next[0].id}) ready to process`);
532
+ } else {
533
+ try {
534
+ await this.db.sequelize.transaction(
535
+ {
536
+ isolationLevel: this.db.options.dialect === "sqlite" ? [][0] : import_sequelize.Transaction.ISOLATION_LEVELS.REPEATABLE_READ
489
537
  },
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();
538
+ async (transaction) => {
539
+ const execution = await this.db.getRepository("executions").findOne({
540
+ filter: {
541
+ status: import_constants.EXECUTION_STATUS.QUEUEING,
542
+ "workflow.enabled": true
543
+ },
544
+ sort: "id",
545
+ transaction
546
+ });
547
+ if (execution) {
548
+ this.getLogger(execution.workflowId).info(`execution (${execution.id}) fetched from db`);
549
+ await execution.update(
550
+ {
551
+ status: import_constants.EXECUTION_STATUS.STARTED
552
+ },
553
+ { transaction }
554
+ );
555
+ execution.workflow = this.enabledCache.get(execution.workflowId);
556
+ next = [execution];
557
+ } else {
558
+ this.getLogger("dispatcher").info(`no execution in db queued to process`);
559
+ }
560
+ }
561
+ );
562
+ } catch (error) {
563
+ this.getLogger("dispatcher").error(`fetching execution from db failed: ${error.message}`, { error });
505
564
  }
506
565
  }
566
+ if (next) {
567
+ await this.process(...next);
568
+ }
569
+ this.executing = null;
570
+ if (next || this.pending.length) {
571
+ this.getLogger("dispatcher").info(`last process finished, will do another dispatch`);
572
+ this.dispatch();
573
+ }
507
574
  })();
508
575
  }
576
+ createProcessor(execution, options = {}) {
577
+ return new import_Processor.default(execution, { ...options, plugin: this });
578
+ }
509
579
  async process(execution, job, options = {}) {
510
580
  var _a, _b;
581
+ const logger = this.getLogger(execution.workflowId);
511
582
  if (execution.status === import_constants.EXECUTION_STATUS.QUEUEING) {
512
583
  const transaction = await this.useDataSourceTransaction("main", options.transaction);
513
584
  await execution.update({ status: import_constants.EXECUTION_STATUS.STARTED }, { transaction });
585
+ logger.info(`queueing execution (${execution.id}) from pending list updated to started`);
514
586
  }
515
- const logger = this.getLogger(execution.workflowId);
516
587
  const processor = this.createProcessor(execution, options);
517
588
  logger.info(`execution (${execution.id}) ${job ? "resuming" : "starting"}...`);
518
589
  try {
519
590
  await (job ? processor.resume(job) : processor.start());
520
591
  logger.info(`execution (${execution.id}) finished with status: ${execution.status}`, { execution });
521
592
  if (execution.status && ((_b = (_a = execution.workflow.options) == null ? void 0 : _a.deleteExecutionOnStatus) == null ? void 0 : _b.includes(execution.status))) {
522
- await execution.destroy();
593
+ await execution.destroy({ transaction: processor.mainTransaction });
523
594
  }
524
595
  } catch (err) {
525
596
  logger.error(`execution (${execution.id}) error: ${err.message}`, err);
526
597
  }
527
598
  return processor;
528
599
  }
529
- async execute(workflow, context, options = {}) {
600
+ async execute(workflow, values, options = {}) {
530
601
  const trigger = this.triggers.get(workflow.type);
531
602
  if (!trigger) {
532
603
  throw new Error(`trigger type "${workflow.type}" of workflow ${workflow.id} is not registered`);
@@ -534,7 +605,7 @@ class PluginWorkflowServer extends import_server.Plugin {
534
605
  if (!trigger.execute) {
535
606
  throw new Error(`"execute" method of trigger ${workflow.type} is not implemented`);
536
607
  }
537
- return trigger.execute(workflow, context, options);
608
+ return trigger.execute(workflow, values, options);
538
609
  }
539
610
  /**
540
611
  * @experimental
@@ -109,6 +109,7 @@ export default class Processor {
109
109
  };
110
110
  $system: {};
111
111
  $scopes: {};
112
+ $env: {};
112
113
  };
113
114
  /**
114
115
  * @experimental
@@ -117,11 +117,11 @@ class Processor {
117
117
  execution,
118
118
  options: { plugin }
119
119
  } = this;
120
- if (!execution.workflow) {
121
- execution.workflow = plugin.enabledCache.get(execution.workflowId);
122
- }
123
120
  this.mainTransaction = plugin.useDataSourceTransaction("main", this.transaction);
124
121
  const transaction = this.mainTransaction;
122
+ if (!execution.workflow) {
123
+ execution.workflow = plugin.enabledCache.get(execution.workflowId) || await execution.getWorkflow({ transaction });
124
+ }
125
125
  const nodes = await execution.workflow.getNodes({ transaction });
126
126
  this.makeNodes(nodes);
127
127
  const jobs = await execution.getJobs({
@@ -212,7 +212,7 @@ class Processor {
212
212
  if (parentNode) {
213
213
  this.logger.debug(`not on main, recall to parent entry node (${node.id})})`);
214
214
  await this.recall(parentNode, job);
215
- return job;
215
+ return null;
216
216
  }
217
217
  return this.exit(job.status);
218
218
  }
@@ -369,7 +369,8 @@ class Processor {
369
369
  $context: this.execution.context,
370
370
  $jobsMapByNodeKey: this.jobsMapByNodeKey,
371
371
  $system: systemFns,
372
- $scopes
372
+ $scopes,
373
+ $env: this.options.plugin.app.environment.getVariables()
373
374
  };
374
375
  }
375
376
  /**
@@ -15,5 +15,4 @@ export declare function sync(context: Context, next: any): Promise<void>;
15
15
  * @deprecated
16
16
  * Keep for action trigger compatibility
17
17
  */
18
- export declare function trigger(context: Context, next: any): Promise<any>;
19
18
  export declare function execute(context: Context, next: any): Promise<any>;
@@ -40,7 +40,6 @@ __export(workflows_exports, {
40
40
  execute: () => execute,
41
41
  revision: () => revision,
42
42
  sync: () => sync,
43
- trigger: () => trigger,
44
43
  update: () => update
45
44
  });
46
45
  module.exports = __toCommonJS(workflows_exports);
@@ -116,12 +115,12 @@ async function sync(context, next) {
116
115
  context.status = 204;
117
116
  await next();
118
117
  }
119
- async function trigger(context, next) {
120
- return next();
121
- }
122
118
  async function execute(context, next) {
123
119
  const plugin = context.app.pm.get(import_Plugin.default);
124
- const { filterByTk, autoRevision } = context.action.params;
120
+ const { filterByTk, values, autoRevision } = context.action.params;
121
+ if (!values) {
122
+ return context.throw(400, "values is required");
123
+ }
125
124
  if (!filterByTk) {
126
125
  return context.throw(400, "filterByTk is required");
127
126
  }
@@ -137,7 +136,7 @@ async function execute(context, next) {
137
136
  const { executed } = workflow;
138
137
  let processor;
139
138
  try {
140
- processor = await plugin.execute(workflow, context, { manually: true });
139
+ processor = await plugin.execute(workflow, values, { manually: true });
141
140
  if (!processor) {
142
141
  return context.throw(400, "workflow not triggered");
143
142
  }
@@ -174,6 +173,5 @@ async function execute(context, next) {
174
173
  execute,
175
174
  revision,
176
175
  sync,
177
- trigger,
178
176
  update
179
177
  });
@@ -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
  }
@@ -68,6 +68,10 @@ class CollectionTrigger extends import__.default {
68
68
  events = /* @__PURE__ */ new Map();
69
69
  // async function, should return promise
70
70
  static async handler(workflow, data, options) {
71
+ const { skipWorkflow = false, stack } = options.context ?? {};
72
+ if (skipWorkflow) {
73
+ return;
74
+ }
71
75
  const [dataSourceName] = (0, import_data_source_manager.parseCollectionName)(workflow.config.collection);
72
76
  const transaction = this.workflow.useDataSourceTransaction(dataSourceName, options.transaction);
73
77
  const ctx = await this.prepare(workflow, data, { ...options, transaction });
@@ -76,15 +80,16 @@ class CollectionTrigger extends import__.default {
76
80
  }
77
81
  if (workflow.sync) {
78
82
  await this.workflow.trigger(workflow, ctx, {
79
- transaction
83
+ transaction,
84
+ stack
80
85
  });
81
86
  } else {
82
87
  if (transaction) {
83
88
  transaction.afterCommit(() => {
84
- this.workflow.trigger(workflow, ctx);
89
+ this.workflow.trigger(workflow, ctx, { stack });
85
90
  });
86
91
  } else {
87
- this.workflow.trigger(workflow, ctx);
92
+ this.workflow.trigger(workflow, ctx, { stack });
88
93
  }
89
94
  }
90
95
  }
@@ -95,13 +100,24 @@ class CollectionTrigger extends import__.default {
95
100
  const collection = collectionManager.getCollection(collectionName);
96
101
  const { transaction, context } = options;
97
102
  const { repository, filterTargetKey } = collection;
98
- if (data instanceof import_database.Model && changed && changed.length && changed.filter((name) => {
103
+ let target = data;
104
+ let filterByTk;
105
+ let loadNeeded = false;
106
+ if (target && typeof target === "object") {
107
+ filterByTk = Array.isArray(filterTargetKey) ? (0, import_lodash.pick)(
108
+ target,
109
+ filterTargetKey.sort((a, b) => a.localeCompare(b))
110
+ ) : target[filterTargetKey];
111
+ } else {
112
+ filterByTk = target;
113
+ loadNeeded = true;
114
+ }
115
+ if (target instanceof import_database.Model && changed && changed.length && changed.filter((name) => {
99
116
  const field = collection.getField(name);
100
117
  return field && !["linkTo", "hasOne", "hasMany", "belongsToMany"].includes(field.options.type);
101
- }).every((name) => !data.changedWithAssociations(getFieldRawName(collection, name)))) {
118
+ }).every((name) => !target.changedWithAssociations(getFieldRawName(collection, name)))) {
102
119
  return null;
103
120
  }
104
- const filterByTk = Array.isArray(filterTargetKey) ? (0, import_lodash.pick)(data, filterTargetKey) : { [filterTargetKey]: data[filterTargetKey] };
105
121
  if ((0, import_utils.isValidFilter)(condition) && !(mode & MODE_BITMAP.DESTROY)) {
106
122
  const count = await repository.count({
107
123
  filterByTk,
@@ -113,22 +129,20 @@ class CollectionTrigger extends import__.default {
113
129
  return null;
114
130
  }
115
131
  }
116
- let result = data;
117
- if ((appends == null ? void 0 : appends.length) && !(mode & MODE_BITMAP.DESTROY)) {
132
+ if (loadNeeded || (appends == null ? void 0 : appends.length) && !(mode & MODE_BITMAP.DESTROY)) {
118
133
  const includeFields = appends.reduce((set, field) => {
119
134
  set.add(field.split(".")[0]);
120
135
  set.add(field);
121
136
  return set;
122
137
  }, /* @__PURE__ */ new Set());
123
- result = await repository.findOne({
138
+ target = await repository.findOne({
124
139
  filterByTk,
125
140
  appends: Array.from(includeFields),
126
141
  transaction
127
142
  });
128
143
  }
129
144
  return {
130
- data: (0, import_utils2.toJSON)(result),
131
- stack: context == null ? void 0 : context.stack
145
+ data: (0, import_utils2.toJSON)(target)
132
146
  };
133
147
  }
134
148
  on(workflow) {
@@ -182,26 +196,27 @@ class CollectionTrigger extends import__.default {
182
196
  }
183
197
  }
184
198
  }
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);
199
+ // async validateEvent(workflow: WorkflowModel, context: any, options: Transactionable): Promise<boolean> {
200
+ // if (context.stack) {
201
+ // const existed = await workflow.countExecutions({
202
+ // where: {
203
+ // id: context.stack,
204
+ // },
205
+ // transaction: options.transaction,
206
+ // });
207
+ // if (existed) {
208
+ // this.workflow
209
+ // .getLogger(workflow.id)
210
+ // .warn(
211
+ // `workflow ${workflow.id} has already been triggered in stack executions (${context.stack}), and newly triggering will be skipped.`,
212
+ // );
213
+ // return false;
214
+ // }
215
+ // }
216
+ // return true;
217
+ // }
218
+ async execute(workflow, values, options) {
219
+ const ctx = await this.prepare(workflow, values == null ? void 0 : values.data, options);
205
220
  const [dataSourceName] = (0, import_data_source_manager.parseCollectionName)(workflow.config.collection);
206
221
  const { transaction } = options;
207
222
  return this.workflow.trigger(workflow, ctx, {
@@ -209,4 +224,12 @@ class CollectionTrigger extends import__.default {
209
224
  transaction: this.workflow.useDataSourceTransaction(dataSourceName, transaction)
210
225
  });
211
226
  }
227
+ validateContext(values) {
228
+ if (!values.data) {
229
+ return {
230
+ data: "Data is required"
231
+ };
232
+ }
233
+ return null;
234
+ }
212
235
  }
@@ -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
  }