@manablox/workflows 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -251,6 +251,7 @@ export declare class WorkflowService {
251
251
  };
252
252
  }, userAgent: string | null): Promise<PushSubscriptionRow>;
253
253
  unsubscribe(userId: string, endpoint: string): Promise<void>;
254
+ private audit;
254
255
  private find;
255
256
  private validationEnvironment;
256
257
  }
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
+ import { ManabloxError, WORKFLOW_CONDITION_OPERATORS, WORKFLOW_CONDITION_OPERATOR_LABELS, WORKFLOW_EVENTS, WORKFLOW_EVENT_LABELS, WORKFLOW_STEP_LABELS, WORKFLOW_STEP_TYPES, diffRecords, isEmailAddress, runAsActor, snapshotChanges } from "@manablox/core";
1
2
  import { createHmac, randomUUID } from "node:crypto";
2
- import { ManabloxError, WORKFLOW_CONDITION_OPERATORS, WORKFLOW_CONDITION_OPERATOR_LABELS, WORKFLOW_EVENTS, WORKFLOW_EVENT_LABELS, WORKFLOW_STEP_LABELS, WORKFLOW_STEP_TYPES, isEmailAddress } from "@manablox/core";
3
3
  import nodemailer from "nodemailer";
4
4
  import webpush from "web-push";
5
5
  import { requireInSpace } from "@manablox/services";
@@ -674,11 +674,22 @@ var WorkflowEngine = class {
674
674
  });
675
675
  return;
676
676
  }
677
- const log = [...run.log];
678
- const outcome = await this.runChain(run, workflow, workflow.steps, [], run.cursor, log);
679
- if (outcome.kind === "done") await this.finish(run, workflow, "succeeded", [], log, null);
680
- else if (outcome.kind === "stop") await this.finish(run, workflow, "skipped", outcome.cursor, log, null);
681
- else if (outcome.kind === "fail") await this.finish(run, workflow, "failed", outcome.cursor, log, outcome.error);
677
+ const actor = {
678
+ kind: "workflow",
679
+ id: workflow.id,
680
+ label: workflow.name,
681
+ detail: {
682
+ runId: run.id,
683
+ trigger: run.trigger
684
+ }
685
+ };
686
+ await runAsActor(actor, async () => {
687
+ const log = [...run.log];
688
+ const outcome = await this.runChain(run, workflow, workflow.steps, [], run.cursor, log);
689
+ if (outcome.kind === "done") await this.finish(run, workflow, "succeeded", [], log, null);
690
+ else if (outcome.kind === "stop") await this.finish(run, workflow, "skipped", outcome.cursor, log, null);
691
+ else if (outcome.kind === "fail") await this.finish(run, workflow, "failed", outcome.cursor, log, outcome.error);
692
+ });
682
693
  }
683
694
  /**
684
695
  * Walks one chain — the top level, or a side of a fork — from `resume` when the run is
@@ -791,6 +802,24 @@ var WorkflowEngine = class {
791
802
  manablox: this.manablox,
792
803
  spaceId: workflow.spaceId
793
804
  });
805
+ await this.repos.audit.record({
806
+ spaceId: workflow.spaceId,
807
+ action: "workflowRun.finish",
808
+ targetKind: "workflowRun",
809
+ targetId: run.id,
810
+ targetLabel: workflow.name,
811
+ meta: {
812
+ workflowId: workflow.id,
813
+ status,
814
+ trigger: run.trigger,
815
+ error,
816
+ contentId: run.context.content?.id ?? null,
817
+ steps: log.map((entry) => ({
818
+ step: entry.name || entry.type,
819
+ status: entry.status
820
+ }))
821
+ }
822
+ });
794
823
  }
795
824
  async spaceInfo(spaceId) {
796
825
  const space = await this.repos.spaces.findById(spaceId);
@@ -1088,6 +1117,8 @@ function isHttpUrl(value) {
1088
1117
  const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
1089
1118
  //#endregion
1090
1119
  //#region src/service.ts
1120
+ /** The scheduler's bookkeeping columns change on their own; the log records what people set. */
1121
+ const WORKFLOW_DIFF = { ignore: ["lastScheduledAt", "lastRunAt"] };
1091
1122
  /**
1092
1123
  * Workflows: the rules — a trigger that exists, steps with somewhere to go, a run that
1093
1124
  * belongs to the space it is asked for in — with the engine doing the running.
@@ -1132,24 +1163,35 @@ var WorkflowService = class {
1132
1163
  }
1133
1164
  async create(spaceId, input) {
1134
1165
  const valid = validateWorkflow(input, this.validationEnvironment());
1135
- return this.repos.workflows.create({
1166
+ const row = await this.repos.workflows.create({
1136
1167
  spaceId,
1137
1168
  ...valid
1138
1169
  });
1170
+ await this.audit("workflow.create", row, snapshotChanges(row, "created", WORKFLOW_DIFF));
1171
+ return row;
1139
1172
  }
1140
1173
  async update(spaceId, id, input) {
1141
- await this.find(spaceId, id);
1174
+ const before = await this.find(spaceId, id);
1142
1175
  const valid = validateWorkflow(input, this.validationEnvironment());
1143
- return this.repos.workflows.update(id, valid);
1176
+ const row = await this.repos.workflows.update(id, valid);
1177
+ await this.audit("workflow.update", row, diffRecords(before, row, WORKFLOW_DIFF));
1178
+ return row;
1144
1179
  }
1145
1180
  /** The on/off switch, kept apart from a full save so the list can flip it. */
1146
1181
  async setEnabled(spaceId, id, enabled) {
1147
- await this.find(spaceId, id);
1148
- return this.repos.workflows.update(id, { enabled });
1182
+ const before = await this.find(spaceId, id);
1183
+ const row = await this.repos.workflows.update(id, { enabled });
1184
+ await this.audit("workflow.setEnabled", row, [{
1185
+ path: "enabled",
1186
+ from: before.enabled,
1187
+ to: row.enabled
1188
+ }]);
1189
+ return row;
1149
1190
  }
1150
1191
  async delete(spaceId, id) {
1151
- await this.find(spaceId, id);
1192
+ const row = await this.find(spaceId, id);
1152
1193
  await this.repos.workflows.delete(id);
1194
+ await this.audit("workflow.delete", row, snapshotChanges(row, "deleted", WORKFLOW_DIFF));
1153
1195
  }
1154
1196
  async runs(spaceId, id, limit = 50) {
1155
1197
  await this.find(spaceId, id);
@@ -1168,7 +1210,13 @@ var WorkflowService = class {
1168
1210
  let document = null;
1169
1211
  if (contentId) document = requireInSpace(await this.repos.content.findById(contentId), spaceId, "content.notFound", { id: contentId });
1170
1212
  else if (workflow.trigger.kind === "event") throw ManabloxError.badRequest("workflow.run.documentRequired");
1171
- return this.engine.runManually(workflow, document);
1213
+ const run = await this.engine.runManually(workflow, document);
1214
+ await this.audit("workflow.runNow", workflow, [], {
1215
+ runId: run.id,
1216
+ status: run.status,
1217
+ contentId: document?.id ?? null
1218
+ });
1219
+ return run;
1172
1220
  }
1173
1221
  subscriptions(userId) {
1174
1222
  return this.repos.workflows.subscriptionsOf(userId);
@@ -1185,6 +1233,17 @@ var WorkflowService = class {
1185
1233
  unsubscribe(userId, endpoint) {
1186
1234
  return this.repos.workflows.unsubscribe(userId, endpoint);
1187
1235
  }
1236
+ audit(action, workflow, changes, meta = null) {
1237
+ return this.repos.audit.record({
1238
+ spaceId: workflow.spaceId,
1239
+ action,
1240
+ targetKind: "workflow",
1241
+ targetId: workflow.id,
1242
+ targetLabel: workflow.name,
1243
+ changes,
1244
+ meta
1245
+ });
1246
+ }
1188
1247
  async find(spaceId, id) {
1189
1248
  return requireInSpace(await this.repos.workflows.findById(id), spaceId, "workflow.notFound", { id });
1190
1249
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manablox/workflows",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -11,15 +11,15 @@
11
11
  "main": "./dist/index.js",
12
12
  "types": "./dist/index.d.ts",
13
13
  "dependencies": {
14
- "@manablox/core": "0.3.0",
15
- "@manablox/db": "0.3.0",
14
+ "@manablox/core": "0.4.0",
15
+ "@manablox/db": "0.4.0",
16
16
  "nodemailer": "^10.0.0",
17
17
  "web-push": "^3.6.7"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@manablox/config-typescript": "0.0.0",
21
- "@manablox/fields": "0.3.0",
22
- "@manablox/services": "0.3.0",
21
+ "@manablox/fields": "0.4.0",
22
+ "@manablox/services": "0.4.0",
23
23
  "@types/node": "^26.4.1",
24
24
  "@types/nodemailer": "^8.0.1",
25
25
  "@types/web-push": "^3.6.4",