@acmekit/workflow-engine-redis 2.13.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.
- package/README.md +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/loaders/index.d.ts +3 -0
- package/dist/loaders/index.d.ts.map +1 -0
- package/dist/loaders/index.js +11 -0
- package/dist/loaders/index.js.map +1 -0
- package/dist/loaders/redis.d.ts +4 -0
- package/dist/loaders/redis.d.ts.map +1 -0
- package/dist/loaders/redis.js +113 -0
- package/dist/loaders/redis.js.map +1 -0
- package/dist/loaders/utils.d.ts +6 -0
- package/dist/loaders/utils.d.ts.map +1 -0
- package/dist/loaders/utils.js +11 -0
- package/dist/loaders/utils.js.map +1 -0
- package/dist/migrations/Migration20231228143900.d.ts +6 -0
- package/dist/migrations/Migration20231228143900.d.ts.map +1 -0
- package/dist/migrations/Migration20231228143900.js +40 -0
- package/dist/migrations/Migration20231228143900.js.map +1 -0
- package/dist/migrations/Migration20241206123341.d.ts +5 -0
- package/dist/migrations/Migration20241206123341.d.ts.map +1 -0
- package/dist/migrations/Migration20241206123341.js +19 -0
- package/dist/migrations/Migration20241206123341.js.map +1 -0
- package/dist/migrations/Migration20250120111059.d.ts +6 -0
- package/dist/migrations/Migration20250120111059.d.ts.map +1 -0
- package/dist/migrations/Migration20250120111059.js +14 -0
- package/dist/migrations/Migration20250120111059.js.map +1 -0
- package/dist/migrations/Migration20250128174354.d.ts +6 -0
- package/dist/migrations/Migration20250128174354.d.ts.map +1 -0
- package/dist/migrations/Migration20250128174354.js +24 -0
- package/dist/migrations/Migration20250128174354.js.map +1 -0
- package/dist/migrations/Migration20250505101505.d.ts +6 -0
- package/dist/migrations/Migration20250505101505.d.ts.map +1 -0
- package/dist/migrations/Migration20250505101505.js +40 -0
- package/dist/migrations/Migration20250505101505.js.map +1 -0
- package/dist/migrations/Migration20250819110923.d.ts +6 -0
- package/dist/migrations/Migration20250819110923.d.ts.map +1 -0
- package/dist/migrations/Migration20250819110923.js +14 -0
- package/dist/migrations/Migration20250819110923.js.map +1 -0
- package/dist/migrations/Migration20250819110924.d.ts +6 -0
- package/dist/migrations/Migration20250819110924.d.ts.map +1 -0
- package/dist/migrations/Migration20250819110924.js +16 -0
- package/dist/migrations/Migration20250819110924.js.map +1 -0
- package/dist/migrations/Migration20250908080326.d.ts +6 -0
- package/dist/migrations/Migration20250908080326.d.ts.map +1 -0
- package/dist/migrations/Migration20250908080326.js +20 -0
- package/dist/migrations/Migration20250908080326.js.map +1 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +6 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/workflow-execution.d.ts +12 -0
- package/dist/models/workflow-execution.d.ts.map +1 -0
- package/dist/models/workflow-execution.js +60 -0
- package/dist/models/workflow-execution.js.map +1 -0
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +26 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +19 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/workflow-orchestrator.d.ts +101 -0
- package/dist/services/workflow-orchestrator.d.ts.map +1 -0
- package/dist/services/workflow-orchestrator.js +660 -0
- package/dist/services/workflow-orchestrator.js.map +1 -0
- package/dist/services/workflows-module.d.ts +165 -0
- package/dist/services/workflows-module.d.ts.map +1 -0
- package/dist/services/workflows-module.js +212 -0
- package/dist/services/workflows-module.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/index.d.ts +138 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +18 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/workflow-orchestrator-storage.d.ts +72 -0
- package/dist/utils/workflow-orchestrator-storage.d.ts.map +1 -0
- package/dist/utils/workflow-orchestrator-storage.js +621 -0
- package/dist/utils/workflow-orchestrator-storage.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var _WorkflowOrchestratorService_logger;
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.WorkflowOrchestratorService = void 0;
|
|
16
|
+
const orchestration_1 = require("@acmekit/framework/orchestration");
|
|
17
|
+
const utils_1 = require("@acmekit/framework/utils");
|
|
18
|
+
const workflows_sdk_1 = require("@acmekit/framework/workflows-sdk");
|
|
19
|
+
const ulid_1 = require("ulid");
|
|
20
|
+
const AnySubscriber = "any";
|
|
21
|
+
class WorkflowOrchestratorService {
|
|
22
|
+
constructor({ dataLoaderOnly, redisDistributedTransactionStorage, redisPublisher, redisSubscriber, sharedContainer, }) {
|
|
23
|
+
this.instanceId = (0, ulid_1.ulid)();
|
|
24
|
+
_WorkflowOrchestratorService_logger.set(this, void 0);
|
|
25
|
+
this.container_ = sharedContainer;
|
|
26
|
+
this.redisPublisher = redisPublisher;
|
|
27
|
+
this.redisSubscriber = redisSubscriber;
|
|
28
|
+
__classPrivateFieldSet(this, _WorkflowOrchestratorService_logger, this.container_.resolve("logger", { allowUnregistered: true }) ?? console, "f");
|
|
29
|
+
redisDistributedTransactionStorage.setWorkflowOrchestratorService(this);
|
|
30
|
+
if (!dataLoaderOnly) {
|
|
31
|
+
orchestration_1.DistributedTransaction.setStorage(redisDistributedTransactionStorage);
|
|
32
|
+
orchestration_1.WorkflowScheduler.setStorage(redisDistributedTransactionStorage);
|
|
33
|
+
}
|
|
34
|
+
this.redisDistributedTransactionStorage_ =
|
|
35
|
+
redisDistributedTransactionStorage;
|
|
36
|
+
this.redisSubscriber.on("message", async (channel, message) => {
|
|
37
|
+
const workflowId = channel.split(":")[1];
|
|
38
|
+
if (!WorkflowOrchestratorService.subscribers.has(workflowId))
|
|
39
|
+
return;
|
|
40
|
+
try {
|
|
41
|
+
const { instanceId, data } = JSON.parse(message);
|
|
42
|
+
await this.notify(data, false, instanceId);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
__classPrivateFieldGet(this, _WorkflowOrchestratorService_logger, "f").error(`Failed to process Redis message: ${error}`);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async onApplicationShutdown() {
|
|
50
|
+
await this.redisDistributedTransactionStorage_.onApplicationShutdown();
|
|
51
|
+
}
|
|
52
|
+
async onApplicationPrepareShutdown() {
|
|
53
|
+
// eslint-disable-next-line max-len
|
|
54
|
+
await this.redisDistributedTransactionStorage_.onApplicationPrepareShutdown();
|
|
55
|
+
}
|
|
56
|
+
async onApplicationStart() {
|
|
57
|
+
await this.redisDistributedTransactionStorage_.onApplicationStart();
|
|
58
|
+
}
|
|
59
|
+
async triggerParentStep(transaction, result, errors) {
|
|
60
|
+
const metadata = transaction.flow.metadata;
|
|
61
|
+
const { parentStepIdempotencyKey, cancelingFromParentStep } = metadata ?? {};
|
|
62
|
+
if (cancelingFromParentStep) {
|
|
63
|
+
/**
|
|
64
|
+
* If the sub workflow is cancelling from a parent step, we don't want to trigger the parent
|
|
65
|
+
* step.
|
|
66
|
+
*/
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (parentStepIdempotencyKey) {
|
|
70
|
+
const hasFailed = [
|
|
71
|
+
utils_1.TransactionState.REVERTED,
|
|
72
|
+
utils_1.TransactionState.FAILED,
|
|
73
|
+
].includes(transaction.flow.state);
|
|
74
|
+
if (hasFailed) {
|
|
75
|
+
await this.setStepFailure({
|
|
76
|
+
idempotencyKey: parentStepIdempotencyKey,
|
|
77
|
+
stepResponse: errors,
|
|
78
|
+
options: {
|
|
79
|
+
logOnError: true,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
await this.setStepSuccess({
|
|
85
|
+
idempotencyKey: parentStepIdempotencyKey,
|
|
86
|
+
stepResponse: result,
|
|
87
|
+
options: {
|
|
88
|
+
logOnError: true,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async run(workflowIdOrWorkflow, options) {
|
|
95
|
+
const { input, transactionId, resultFrom, logOnError, events: eventHandlers, container, } = options ?? {};
|
|
96
|
+
let { throwOnError, context } = options ?? {};
|
|
97
|
+
throwOnError ??= true;
|
|
98
|
+
context ??= {};
|
|
99
|
+
context.transactionId = transactionId ?? "auto-" + (0, ulid_1.ulid)();
|
|
100
|
+
const workflowId = (0, utils_1.isString)(workflowIdOrWorkflow)
|
|
101
|
+
? workflowIdOrWorkflow
|
|
102
|
+
: workflowIdOrWorkflow.getName();
|
|
103
|
+
if (!workflowId) {
|
|
104
|
+
throw new utils_1.AcmeKitError(utils_1.AcmeKitError.Types.NOT_FOUND, `Workflow ID is required`);
|
|
105
|
+
}
|
|
106
|
+
const events = this.buildWorkflowEvents({
|
|
107
|
+
customEventHandlers: eventHandlers,
|
|
108
|
+
workflowId,
|
|
109
|
+
transactionId: context.transactionId,
|
|
110
|
+
});
|
|
111
|
+
const exportedWorkflow = workflows_sdk_1.AcmeKitWorkflow.getWorkflow(workflowId);
|
|
112
|
+
if (!exportedWorkflow) {
|
|
113
|
+
throw new utils_1.AcmeKitError(utils_1.AcmeKitError.Types.NOT_FOUND, `Workflow with id "${workflowId}" not found.`);
|
|
114
|
+
}
|
|
115
|
+
const { onFinish, ...restEvents } = events;
|
|
116
|
+
const originalOnFinishHandler = events.onFinish;
|
|
117
|
+
const ret = await exportedWorkflow.run({
|
|
118
|
+
input,
|
|
119
|
+
throwOnError: false,
|
|
120
|
+
logOnError,
|
|
121
|
+
resultFrom,
|
|
122
|
+
context,
|
|
123
|
+
events: restEvents,
|
|
124
|
+
container: container ?? this.container_,
|
|
125
|
+
});
|
|
126
|
+
const hasFinished = ret.transaction.hasFinished();
|
|
127
|
+
const metadata = ret.transaction.getFlow().metadata;
|
|
128
|
+
const { parentStepIdempotencyKey } = metadata ?? {};
|
|
129
|
+
const hasFailed = [
|
|
130
|
+
utils_1.TransactionState.REVERTED,
|
|
131
|
+
utils_1.TransactionState.FAILED,
|
|
132
|
+
].includes(ret.transaction.getFlow().state);
|
|
133
|
+
const acknowledgement = {
|
|
134
|
+
transactionId: context.transactionId,
|
|
135
|
+
workflowId: workflowId,
|
|
136
|
+
parentStepIdempotencyKey,
|
|
137
|
+
hasFinished,
|
|
138
|
+
hasFailed,
|
|
139
|
+
};
|
|
140
|
+
if (hasFinished) {
|
|
141
|
+
const { result, errors } = ret;
|
|
142
|
+
await originalOnFinishHandler({
|
|
143
|
+
transaction: ret.transaction,
|
|
144
|
+
result,
|
|
145
|
+
errors,
|
|
146
|
+
});
|
|
147
|
+
await this.triggerParentStep(ret.transaction, result, errors);
|
|
148
|
+
}
|
|
149
|
+
if (throwOnError && (ret.thrownError || ret.errors?.length)) {
|
|
150
|
+
if (ret.thrownError) {
|
|
151
|
+
throw ret.thrownError;
|
|
152
|
+
}
|
|
153
|
+
throw ret.errors[0].error;
|
|
154
|
+
}
|
|
155
|
+
return { acknowledgement, ...ret };
|
|
156
|
+
}
|
|
157
|
+
async cancel(workflowIdOrWorkflow, options) {
|
|
158
|
+
const { transactionId, logOnError, events: eventHandlers, container, } = options ?? {};
|
|
159
|
+
let { throwOnError, context } = options ?? {};
|
|
160
|
+
throwOnError ??= true;
|
|
161
|
+
context ??= {};
|
|
162
|
+
const workflowId = (0, utils_1.isString)(workflowIdOrWorkflow)
|
|
163
|
+
? workflowIdOrWorkflow
|
|
164
|
+
: workflowIdOrWorkflow.getName();
|
|
165
|
+
if (!workflowId) {
|
|
166
|
+
throw new Error("Workflow ID is required");
|
|
167
|
+
}
|
|
168
|
+
if (!transactionId) {
|
|
169
|
+
throw new Error("Transaction ID is required");
|
|
170
|
+
}
|
|
171
|
+
const exportedWorkflow = workflows_sdk_1.AcmeKitWorkflow.getWorkflow(workflowId);
|
|
172
|
+
if (!exportedWorkflow) {
|
|
173
|
+
throw new utils_1.AcmeKitError(utils_1.AcmeKitError.Types.NOT_FOUND, `Workflow with id "${workflowId}" not found.`);
|
|
174
|
+
}
|
|
175
|
+
const transaction = await this.getRunningTransaction(workflowId, transactionId, { ...options, isCancelling: true });
|
|
176
|
+
if (!transaction) {
|
|
177
|
+
if (!throwOnError) {
|
|
178
|
+
return {
|
|
179
|
+
acknowledgement: {
|
|
180
|
+
transactionId,
|
|
181
|
+
workflowId,
|
|
182
|
+
exists: false,
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
throw new Error("Transaction not found");
|
|
187
|
+
}
|
|
188
|
+
const events = this.buildWorkflowEvents({
|
|
189
|
+
customEventHandlers: eventHandlers,
|
|
190
|
+
workflowId,
|
|
191
|
+
transactionId: transactionId,
|
|
192
|
+
});
|
|
193
|
+
const { onFinish, ...restEvents } = events;
|
|
194
|
+
const originalOnFinishHandler = events.onFinish;
|
|
195
|
+
const ret = await exportedWorkflow.cancel({
|
|
196
|
+
transaction,
|
|
197
|
+
throwOnError: false,
|
|
198
|
+
logOnError,
|
|
199
|
+
context,
|
|
200
|
+
events: restEvents,
|
|
201
|
+
container: container ?? this.container_,
|
|
202
|
+
});
|
|
203
|
+
const hasFinished = ret.transaction.hasFinished();
|
|
204
|
+
const metadata = ret.transaction.getFlow().metadata;
|
|
205
|
+
const { parentStepIdempotencyKey } = metadata ?? {};
|
|
206
|
+
const transactionState = ret.transaction.getFlow().state;
|
|
207
|
+
const hasFailed = [utils_1.TransactionState.FAILED].includes(transactionState);
|
|
208
|
+
const acknowledgement = {
|
|
209
|
+
transactionId: transaction.transactionId,
|
|
210
|
+
workflowId: workflowId,
|
|
211
|
+
parentStepIdempotencyKey,
|
|
212
|
+
hasFinished,
|
|
213
|
+
hasFailed,
|
|
214
|
+
exists: true,
|
|
215
|
+
};
|
|
216
|
+
if (hasFinished) {
|
|
217
|
+
const { result, errors } = ret;
|
|
218
|
+
await originalOnFinishHandler({
|
|
219
|
+
transaction: ret.transaction,
|
|
220
|
+
result,
|
|
221
|
+
errors,
|
|
222
|
+
});
|
|
223
|
+
await this.triggerParentStep(ret.transaction, result, errors);
|
|
224
|
+
}
|
|
225
|
+
if (throwOnError && (ret.thrownError || ret.errors?.length)) {
|
|
226
|
+
if (ret.thrownError) {
|
|
227
|
+
throw ret.thrownError;
|
|
228
|
+
}
|
|
229
|
+
throw ret.errors[0].error;
|
|
230
|
+
}
|
|
231
|
+
return { acknowledgement, ...ret };
|
|
232
|
+
}
|
|
233
|
+
async getRunningTransaction(workflowId, transactionId, context) {
|
|
234
|
+
if (!workflowId) {
|
|
235
|
+
throw new Error("Workflow ID is required");
|
|
236
|
+
}
|
|
237
|
+
if (!transactionId) {
|
|
238
|
+
throw new Error("TransactionId ID is required");
|
|
239
|
+
}
|
|
240
|
+
context ??= {};
|
|
241
|
+
const exportedWorkflow = workflows_sdk_1.AcmeKitWorkflow.getWorkflow(workflowId);
|
|
242
|
+
if (!exportedWorkflow) {
|
|
243
|
+
throw new Error(`Workflow with id "${workflowId}" not found.`);
|
|
244
|
+
}
|
|
245
|
+
const flow = exportedWorkflow();
|
|
246
|
+
const transaction = await flow.getRunningTransaction(transactionId, context);
|
|
247
|
+
return transaction;
|
|
248
|
+
}
|
|
249
|
+
async retryStep({ idempotencyKey, options, }) {
|
|
250
|
+
const { context, logOnError, container, events: eventHandlers, } = options ?? {};
|
|
251
|
+
let { throwOnError } = options ?? {};
|
|
252
|
+
throwOnError ??= true;
|
|
253
|
+
const [idempotencyKey_, { workflowId, transactionId }] = this.buildIdempotencyKeyAndParts(idempotencyKey);
|
|
254
|
+
const exportedWorkflow = workflows_sdk_1.AcmeKitWorkflow.getWorkflow(workflowId);
|
|
255
|
+
if (!exportedWorkflow) {
|
|
256
|
+
throw new Error(`Workflow with id "${workflowId}" not found.`);
|
|
257
|
+
}
|
|
258
|
+
const events = this.buildWorkflowEvents({
|
|
259
|
+
customEventHandlers: eventHandlers,
|
|
260
|
+
transactionId,
|
|
261
|
+
workflowId,
|
|
262
|
+
});
|
|
263
|
+
const { onFinish, ...restEvents } = events;
|
|
264
|
+
const originalOnFinishHandler = events.onFinish;
|
|
265
|
+
const ret = await exportedWorkflow.retryStep({
|
|
266
|
+
idempotencyKey: idempotencyKey_,
|
|
267
|
+
context,
|
|
268
|
+
throwOnError: false,
|
|
269
|
+
logOnError,
|
|
270
|
+
events: restEvents,
|
|
271
|
+
container: container ?? this.container_,
|
|
272
|
+
});
|
|
273
|
+
if (ret.transaction.hasFinished()) {
|
|
274
|
+
const { result, errors } = ret;
|
|
275
|
+
await originalOnFinishHandler({
|
|
276
|
+
transaction: ret.transaction,
|
|
277
|
+
result,
|
|
278
|
+
errors,
|
|
279
|
+
});
|
|
280
|
+
await this.triggerParentStep(ret.transaction, result, errors);
|
|
281
|
+
}
|
|
282
|
+
if (throwOnError && (ret.thrownError || ret.errors?.length)) {
|
|
283
|
+
if (ret.thrownError) {
|
|
284
|
+
throw ret.thrownError;
|
|
285
|
+
}
|
|
286
|
+
throw ret.errors[0].error;
|
|
287
|
+
}
|
|
288
|
+
return ret;
|
|
289
|
+
}
|
|
290
|
+
async setStepSuccess({ idempotencyKey, stepResponse, options, }) {
|
|
291
|
+
const { context, logOnError, resultFrom, container, events: eventHandlers, } = options ?? {};
|
|
292
|
+
let { throwOnError } = options ?? {};
|
|
293
|
+
throwOnError ??= true;
|
|
294
|
+
const [idempotencyKey_, { workflowId, transactionId }] = this.buildIdempotencyKeyAndParts(idempotencyKey);
|
|
295
|
+
const exportedWorkflow = workflows_sdk_1.AcmeKitWorkflow.getWorkflow(workflowId);
|
|
296
|
+
if (!exportedWorkflow) {
|
|
297
|
+
throw new Error(`Workflow with id "${workflowId}" not found.`);
|
|
298
|
+
}
|
|
299
|
+
const events = this.buildWorkflowEvents({
|
|
300
|
+
customEventHandlers: eventHandlers,
|
|
301
|
+
transactionId,
|
|
302
|
+
workflowId,
|
|
303
|
+
});
|
|
304
|
+
const { onFinish, ...restEvents } = events;
|
|
305
|
+
const originalOnFinishHandler = events.onFinish;
|
|
306
|
+
const ret = await exportedWorkflow.registerStepSuccess({
|
|
307
|
+
idempotencyKey: idempotencyKey_,
|
|
308
|
+
context,
|
|
309
|
+
resultFrom,
|
|
310
|
+
throwOnError: false,
|
|
311
|
+
logOnError,
|
|
312
|
+
events: restEvents,
|
|
313
|
+
response: stepResponse,
|
|
314
|
+
container: container ?? this.container_,
|
|
315
|
+
});
|
|
316
|
+
if (ret.transaction.hasFinished()) {
|
|
317
|
+
const { result, errors } = ret;
|
|
318
|
+
await originalOnFinishHandler({
|
|
319
|
+
transaction: ret.transaction,
|
|
320
|
+
result,
|
|
321
|
+
errors,
|
|
322
|
+
});
|
|
323
|
+
await this.triggerParentStep(ret.transaction, result, errors);
|
|
324
|
+
}
|
|
325
|
+
if (throwOnError && (ret.thrownError || ret.errors?.length)) {
|
|
326
|
+
if (ret.thrownError) {
|
|
327
|
+
throw ret.thrownError;
|
|
328
|
+
}
|
|
329
|
+
throw ret.errors[0].error;
|
|
330
|
+
}
|
|
331
|
+
return ret;
|
|
332
|
+
}
|
|
333
|
+
async setStepFailure({ idempotencyKey, stepResponse, options, }) {
|
|
334
|
+
const { context, logOnError, resultFrom, container, events: eventHandlers, forcePermanentFailure, } = options ?? {};
|
|
335
|
+
let { throwOnError } = options ?? {};
|
|
336
|
+
throwOnError ??= true;
|
|
337
|
+
const [idempotencyKey_, { workflowId, transactionId }] = this.buildIdempotencyKeyAndParts(idempotencyKey);
|
|
338
|
+
const exportedWorkflow = workflows_sdk_1.AcmeKitWorkflow.getWorkflow(workflowId);
|
|
339
|
+
if (!exportedWorkflow) {
|
|
340
|
+
throw new Error(`Workflow with id "${workflowId}" not found.`);
|
|
341
|
+
}
|
|
342
|
+
const events = this.buildWorkflowEvents({
|
|
343
|
+
customEventHandlers: eventHandlers,
|
|
344
|
+
transactionId,
|
|
345
|
+
workflowId,
|
|
346
|
+
});
|
|
347
|
+
const { onFinish, ...restEvents } = events;
|
|
348
|
+
const originalOnFinishHandler = events.onFinish;
|
|
349
|
+
const ret = await exportedWorkflow.registerStepFailure({
|
|
350
|
+
idempotencyKey: idempotencyKey_,
|
|
351
|
+
context,
|
|
352
|
+
resultFrom,
|
|
353
|
+
throwOnError: false,
|
|
354
|
+
logOnError,
|
|
355
|
+
events: restEvents,
|
|
356
|
+
response: stepResponse,
|
|
357
|
+
container: container ?? this.container_,
|
|
358
|
+
forcePermanentFailure,
|
|
359
|
+
});
|
|
360
|
+
if (ret.transaction.hasFinished()) {
|
|
361
|
+
const { result, errors } = ret;
|
|
362
|
+
await originalOnFinishHandler({
|
|
363
|
+
transaction: ret.transaction,
|
|
364
|
+
result,
|
|
365
|
+
errors,
|
|
366
|
+
});
|
|
367
|
+
await this.triggerParentStep(ret.transaction, result, errors);
|
|
368
|
+
}
|
|
369
|
+
if (throwOnError && (ret.thrownError || ret.errors?.length)) {
|
|
370
|
+
if (ret.thrownError) {
|
|
371
|
+
throw ret.thrownError;
|
|
372
|
+
}
|
|
373
|
+
throw ret.errors[0].error;
|
|
374
|
+
}
|
|
375
|
+
return ret;
|
|
376
|
+
}
|
|
377
|
+
subscribe({ workflowId, transactionId, subscriber, subscriberId, }) {
|
|
378
|
+
subscriber._id = subscriberId;
|
|
379
|
+
const subscribers = WorkflowOrchestratorService.subscribers.get(workflowId) ?? new Map();
|
|
380
|
+
// Subscribe instance to redis
|
|
381
|
+
if (!WorkflowOrchestratorService.subscribers.has(workflowId)) {
|
|
382
|
+
void this.redisSubscriber.subscribe(this.getChannelName(workflowId));
|
|
383
|
+
}
|
|
384
|
+
const handlerIndex = (handlers) => {
|
|
385
|
+
return handlers.findIndex((s) => s === subscriber || s._id === subscriberId);
|
|
386
|
+
};
|
|
387
|
+
if (transactionId) {
|
|
388
|
+
const transactionSubscribers = subscribers.get(transactionId) ?? [];
|
|
389
|
+
const subscriberIndex = handlerIndex(transactionSubscribers);
|
|
390
|
+
if (subscriberIndex !== -1) {
|
|
391
|
+
transactionSubscribers.splice(subscriberIndex, 1);
|
|
392
|
+
}
|
|
393
|
+
transactionSubscribers.push(subscriber);
|
|
394
|
+
subscribers.set(transactionId, transactionSubscribers);
|
|
395
|
+
WorkflowOrchestratorService.subscribers.set(workflowId, subscribers);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const workflowSubscribers = subscribers.get(AnySubscriber) ?? [];
|
|
399
|
+
const subscriberIndex = handlerIndex(workflowSubscribers);
|
|
400
|
+
if (subscriberIndex !== -1) {
|
|
401
|
+
workflowSubscribers.splice(subscriberIndex, 1);
|
|
402
|
+
}
|
|
403
|
+
workflowSubscribers.push(subscriber);
|
|
404
|
+
subscribers.set(AnySubscriber, workflowSubscribers);
|
|
405
|
+
WorkflowOrchestratorService.subscribers.set(workflowId, subscribers);
|
|
406
|
+
}
|
|
407
|
+
unsubscribe({ workflowId, transactionId, subscriberOrId, }) {
|
|
408
|
+
const subscribers = WorkflowOrchestratorService.subscribers.get(workflowId);
|
|
409
|
+
if (!subscribers) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const filterSubscribers = (handlers) => {
|
|
413
|
+
return handlers.filter((handler) => {
|
|
414
|
+
return handler._id
|
|
415
|
+
? handler._id !== subscriberOrId
|
|
416
|
+
: handler !== subscriberOrId;
|
|
417
|
+
});
|
|
418
|
+
};
|
|
419
|
+
if (transactionId) {
|
|
420
|
+
const transactionSubscribers = subscribers.get(transactionId);
|
|
421
|
+
if (transactionSubscribers) {
|
|
422
|
+
const newTransactionSubscribers = filterSubscribers(transactionSubscribers);
|
|
423
|
+
if (newTransactionSubscribers.length) {
|
|
424
|
+
subscribers.set(transactionId, newTransactionSubscribers);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
subscribers.delete(transactionId);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
const workflowSubscribers = subscribers.get(AnySubscriber);
|
|
433
|
+
if (workflowSubscribers) {
|
|
434
|
+
const newWorkflowSubscribers = filterSubscribers(workflowSubscribers);
|
|
435
|
+
if (newWorkflowSubscribers.length) {
|
|
436
|
+
subscribers.set(AnySubscriber, newWorkflowSubscribers);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
subscribers.delete(AnySubscriber);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (subscribers.size === 0) {
|
|
444
|
+
WorkflowOrchestratorService.subscribers.delete(workflowId);
|
|
445
|
+
void this.redisSubscriber.unsubscribe(this.getChannelName(workflowId));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
async notify(options, publish = true, instanceId = this.instanceId) {
|
|
449
|
+
if (!publish && instanceId === this.instanceId) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const { workflowId, isFlowAsync } = options;
|
|
453
|
+
// Non-blocking Redis publishing
|
|
454
|
+
if (publish && isFlowAsync) {
|
|
455
|
+
setImmediate(async () => {
|
|
456
|
+
try {
|
|
457
|
+
const channel = this.getChannelName(workflowId);
|
|
458
|
+
const message = JSON.stringify({
|
|
459
|
+
instanceId: this.instanceId,
|
|
460
|
+
data: options,
|
|
461
|
+
});
|
|
462
|
+
await this.redisPublisher.publish(channel, message);
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
__classPrivateFieldGet(this, _WorkflowOrchestratorService_logger, "f").error(`Failed to publish to Redis: ${error}`);
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
// Process subscribers asynchronously
|
|
470
|
+
setImmediate(() => this.processSubscriberNotifications(options));
|
|
471
|
+
}
|
|
472
|
+
async processSubscriberNotifications(options) {
|
|
473
|
+
const { workflowId, transactionId, eventType } = options;
|
|
474
|
+
const subscribers = WorkflowOrchestratorService.subscribers.get(workflowId) ?? new Map();
|
|
475
|
+
const notifySubscribersAsync = async (handlers) => {
|
|
476
|
+
const promises = handlers.map(async (handler) => {
|
|
477
|
+
try {
|
|
478
|
+
const result = handler(options);
|
|
479
|
+
if (result && typeof result === "object" && "then" in result) {
|
|
480
|
+
await result;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
catch (error) {
|
|
484
|
+
__classPrivateFieldGet(this, _WorkflowOrchestratorService_logger, "f").error(`Subscriber error: ${error}`);
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
await (0, utils_1.promiseAll)(promises);
|
|
488
|
+
};
|
|
489
|
+
const tasks = [];
|
|
490
|
+
if (transactionId) {
|
|
491
|
+
const transactionSubscribers = subscribers.get(transactionId) ?? [];
|
|
492
|
+
if (transactionSubscribers.length > 0) {
|
|
493
|
+
tasks.push(notifySubscribersAsync(transactionSubscribers));
|
|
494
|
+
}
|
|
495
|
+
if (eventType === "onFinish") {
|
|
496
|
+
subscribers.delete(transactionId);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const workflowSubscribers = subscribers.get(AnySubscriber) ?? [];
|
|
500
|
+
if (workflowSubscribers.length > 0) {
|
|
501
|
+
tasks.push(notifySubscribersAsync(workflowSubscribers));
|
|
502
|
+
}
|
|
503
|
+
await (0, utils_1.promiseAll)(tasks);
|
|
504
|
+
}
|
|
505
|
+
getChannelName(workflowId) {
|
|
506
|
+
return `orchestrator:${workflowId}`;
|
|
507
|
+
}
|
|
508
|
+
buildWorkflowEvents({ customEventHandlers, workflowId, transactionId, }) {
|
|
509
|
+
const notify = async ({ isFlowAsync, eventType, step, result, response, errors, state, }) => {
|
|
510
|
+
await this.notify({
|
|
511
|
+
isFlowAsync,
|
|
512
|
+
workflowId,
|
|
513
|
+
transactionId,
|
|
514
|
+
eventType,
|
|
515
|
+
response,
|
|
516
|
+
step,
|
|
517
|
+
result,
|
|
518
|
+
errors,
|
|
519
|
+
state,
|
|
520
|
+
});
|
|
521
|
+
};
|
|
522
|
+
return {
|
|
523
|
+
onTimeout: async ({ transaction }) => {
|
|
524
|
+
customEventHandlers?.onTimeout?.({ transaction });
|
|
525
|
+
await notify({
|
|
526
|
+
eventType: "onTimeout",
|
|
527
|
+
isFlowAsync: transaction.getFlow().hasAsyncSteps,
|
|
528
|
+
});
|
|
529
|
+
},
|
|
530
|
+
onBegin: async ({ transaction }) => {
|
|
531
|
+
customEventHandlers?.onBegin?.({ transaction });
|
|
532
|
+
await notify({
|
|
533
|
+
eventType: "onBegin",
|
|
534
|
+
isFlowAsync: transaction.getFlow().hasAsyncSteps,
|
|
535
|
+
});
|
|
536
|
+
},
|
|
537
|
+
onResume: async ({ transaction }) => {
|
|
538
|
+
customEventHandlers?.onResume?.({ transaction });
|
|
539
|
+
await notify({
|
|
540
|
+
eventType: "onResume",
|
|
541
|
+
isFlowAsync: transaction.getFlow().hasAsyncSteps,
|
|
542
|
+
});
|
|
543
|
+
},
|
|
544
|
+
onCompensateBegin: async ({ transaction }) => {
|
|
545
|
+
customEventHandlers?.onCompensateBegin?.({ transaction });
|
|
546
|
+
await notify({
|
|
547
|
+
eventType: "onCompensateBegin",
|
|
548
|
+
isFlowAsync: transaction.getFlow().hasAsyncSteps,
|
|
549
|
+
});
|
|
550
|
+
},
|
|
551
|
+
onFinish: async ({ transaction, result, errors }) => {
|
|
552
|
+
customEventHandlers?.onFinish?.({ transaction, result, errors });
|
|
553
|
+
await notify({
|
|
554
|
+
eventType: "onFinish",
|
|
555
|
+
isFlowAsync: transaction.getFlow().hasAsyncSteps,
|
|
556
|
+
result,
|
|
557
|
+
errors,
|
|
558
|
+
state: transaction.getFlow().state,
|
|
559
|
+
});
|
|
560
|
+
},
|
|
561
|
+
onStepBegin: async ({ step, transaction }) => {
|
|
562
|
+
customEventHandlers?.onStepBegin?.({ step, transaction });
|
|
563
|
+
await notify({
|
|
564
|
+
eventType: "onStepBegin",
|
|
565
|
+
step,
|
|
566
|
+
isFlowAsync: transaction.getFlow().hasAsyncSteps,
|
|
567
|
+
});
|
|
568
|
+
},
|
|
569
|
+
onStepSuccess: async ({ step, transaction }) => {
|
|
570
|
+
const stepName = step.definition.action;
|
|
571
|
+
const response = await (0, workflows_sdk_1.resolveValue)(transaction.getContext().invoke[stepName], transaction);
|
|
572
|
+
customEventHandlers?.onStepSuccess?.({ step, transaction, response });
|
|
573
|
+
await notify({
|
|
574
|
+
eventType: "onStepSuccess",
|
|
575
|
+
step,
|
|
576
|
+
response,
|
|
577
|
+
isFlowAsync: transaction.getFlow().hasAsyncSteps,
|
|
578
|
+
});
|
|
579
|
+
},
|
|
580
|
+
onStepFailure: async ({ step, transaction }) => {
|
|
581
|
+
const stepName = step.definition.action;
|
|
582
|
+
const errors = transaction
|
|
583
|
+
.getErrors(orchestration_1.TransactionHandlerType.INVOKE)
|
|
584
|
+
.filter((err) => err.action === stepName);
|
|
585
|
+
customEventHandlers?.onStepFailure?.({ step, transaction, errors });
|
|
586
|
+
await notify({
|
|
587
|
+
eventType: "onStepFailure",
|
|
588
|
+
step,
|
|
589
|
+
errors,
|
|
590
|
+
isFlowAsync: transaction.getFlow().hasAsyncSteps,
|
|
591
|
+
});
|
|
592
|
+
},
|
|
593
|
+
onStepAwaiting: async ({ step, transaction }) => {
|
|
594
|
+
customEventHandlers?.onStepAwaiting?.({ step, transaction });
|
|
595
|
+
await notify({
|
|
596
|
+
eventType: "onStepAwaiting",
|
|
597
|
+
step,
|
|
598
|
+
isFlowAsync: transaction.getFlow().hasAsyncSteps,
|
|
599
|
+
});
|
|
600
|
+
},
|
|
601
|
+
onCompensateStepSuccess: async ({ step, transaction }) => {
|
|
602
|
+
const stepName = step.definition.action;
|
|
603
|
+
const response = transaction.getContext().compensate[stepName];
|
|
604
|
+
customEventHandlers?.onCompensateStepSuccess?.({
|
|
605
|
+
step,
|
|
606
|
+
transaction,
|
|
607
|
+
response,
|
|
608
|
+
});
|
|
609
|
+
await notify({
|
|
610
|
+
eventType: "onCompensateStepSuccess",
|
|
611
|
+
step,
|
|
612
|
+
response,
|
|
613
|
+
isFlowAsync: transaction.getFlow().hasAsyncSteps,
|
|
614
|
+
});
|
|
615
|
+
},
|
|
616
|
+
onCompensateStepFailure: async ({ step, transaction }) => {
|
|
617
|
+
const stepName = step.definition.action;
|
|
618
|
+
const errors = transaction
|
|
619
|
+
.getErrors(orchestration_1.TransactionHandlerType.COMPENSATE)
|
|
620
|
+
.filter((err) => err.action === stepName);
|
|
621
|
+
customEventHandlers?.onStepFailure?.({ step, transaction, errors });
|
|
622
|
+
await notify({
|
|
623
|
+
eventType: "onCompensateStepFailure",
|
|
624
|
+
step,
|
|
625
|
+
errors,
|
|
626
|
+
isFlowAsync: transaction.getFlow().hasAsyncSteps,
|
|
627
|
+
});
|
|
628
|
+
},
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
buildIdempotencyKeyAndParts(idempotencyKey) {
|
|
632
|
+
const parts = {
|
|
633
|
+
workflowId: "",
|
|
634
|
+
transactionId: "",
|
|
635
|
+
stepId: "",
|
|
636
|
+
action: "invoke",
|
|
637
|
+
};
|
|
638
|
+
let idempotencyKey_ = idempotencyKey;
|
|
639
|
+
const setParts = (workflowId, transactionId, stepId, action) => {
|
|
640
|
+
parts.workflowId = workflowId;
|
|
641
|
+
parts.transactionId = transactionId;
|
|
642
|
+
parts.stepId = stepId;
|
|
643
|
+
parts.action = action;
|
|
644
|
+
};
|
|
645
|
+
if (!(0, utils_1.isString)(idempotencyKey)) {
|
|
646
|
+
const { workflowId, transactionId, stepId, action } = idempotencyKey;
|
|
647
|
+
idempotencyKey_ = [workflowId, transactionId, stepId, action].join(":");
|
|
648
|
+
setParts(workflowId, transactionId, stepId, action);
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
const [workflowId, transactionId, stepId, action] = idempotencyKey_.split(":");
|
|
652
|
+
setParts(workflowId, transactionId, stepId, action);
|
|
653
|
+
}
|
|
654
|
+
return [idempotencyKey_, parts];
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
exports.WorkflowOrchestratorService = WorkflowOrchestratorService;
|
|
658
|
+
_WorkflowOrchestratorService_logger = new WeakMap();
|
|
659
|
+
WorkflowOrchestratorService.subscribers = new Map();
|
|
660
|
+
//# sourceMappingURL=workflow-orchestrator.js.map
|