codehooks-js 1.3.2 → 1.3.4
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/package.json +1 -1
- package/types/index.d.ts +4 -0
- package/types/workflow/engine.d.mts +10 -0
- package/types/workflow/index.d.mts +4 -0
- package/workflow/engine.mjs +46 -27
- package/workflow/index.mjs +3 -0
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -64,6 +64,10 @@ declare class Codehooks {
|
|
|
64
64
|
crudlify: (schema?: {}, options?: {}) => Promise<import("./crudlify/lib/eventhooks.mjs").EventHooks>;
|
|
65
65
|
createWorkflow: (name: any, description: any, steps: any, options?: {}) => {
|
|
66
66
|
definitions: Map<any, any>;
|
|
67
|
+
configure(config: {
|
|
68
|
+
collectionName: string;
|
|
69
|
+
queuePrefix: string;
|
|
70
|
+
}): void;
|
|
67
71
|
getDefinition(stepsName: string, stepName: string): Function;
|
|
68
72
|
validateStepDefinition(step: Function): void;
|
|
69
73
|
handleNextStep(stepsName: string, nextStep: string | null, newState: any, instanceId: string, options: any): Promise<string | string[]>;
|
|
@@ -30,6 +30,16 @@ declare class StepsEngine extends EventEmitter<[never]> {
|
|
|
30
30
|
static getInstance(): StepsEngine;
|
|
31
31
|
constructor();
|
|
32
32
|
definitions: Map<any, any>;
|
|
33
|
+
/**
|
|
34
|
+
* Configure the steps engine
|
|
35
|
+
* @param {Object} config - Configuration object
|
|
36
|
+
* @param {string} config.collectionName - Collection name
|
|
37
|
+
* @param {string} config.queuePrefix - Queue prefix
|
|
38
|
+
*/
|
|
39
|
+
configure(config: {
|
|
40
|
+
collectionName: string;
|
|
41
|
+
queuePrefix: string;
|
|
42
|
+
}): void;
|
|
33
43
|
/**
|
|
34
44
|
* Get the step definition for a specific step
|
|
35
45
|
* @param {string} stepsName - Name of the steps workflow
|
|
@@ -2,6 +2,10 @@ export namespace StepsConfig {
|
|
|
2
2
|
export { setCollectionName };
|
|
3
3
|
export { setQueuePrefix };
|
|
4
4
|
}
|
|
5
|
+
export const configure: (config: {
|
|
6
|
+
collectionName: string;
|
|
7
|
+
queuePrefix: string;
|
|
8
|
+
}) => void;
|
|
5
9
|
export { Steps };
|
|
6
10
|
export default StepsEngine;
|
|
7
11
|
import { setCollectionName } from './engine.mjs';
|
package/workflow/engine.mjs
CHANGED
|
@@ -20,8 +20,8 @@ class StepsEngine extends EventEmitter {
|
|
|
20
20
|
static instance = null;
|
|
21
21
|
|
|
22
22
|
// Configuration
|
|
23
|
-
static collectionName = '
|
|
24
|
-
static queuePrefix = '
|
|
23
|
+
static collectionName = 'workflowdata';
|
|
24
|
+
static queuePrefix = 'workflowqueue';
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Set the collection name for storing steps data
|
|
@@ -47,6 +47,21 @@ class StepsEngine extends EventEmitter {
|
|
|
47
47
|
StepsEngine.queuePrefix = prefix.trim();
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Configure the steps engine
|
|
52
|
+
* @param {Object} config - Configuration object
|
|
53
|
+
* @param {string} config.collectionName - Collection name
|
|
54
|
+
* @param {string} config.queuePrefix - Queue prefix
|
|
55
|
+
*/
|
|
56
|
+
configure(config) {
|
|
57
|
+
if (config.collectionName) {
|
|
58
|
+
StepsEngine.setCollectionName(config.collectionName);
|
|
59
|
+
}
|
|
60
|
+
if (config.queuePrefix) {
|
|
61
|
+
StepsEngine.setQueuePrefix(config.queuePrefix);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
/**
|
|
51
66
|
* Get the singleton instance of StepsEngine
|
|
52
67
|
* @returns {StepsEngine} The singleton instance
|
|
@@ -114,8 +129,6 @@ class StepsEngine extends EventEmitter {
|
|
|
114
129
|
return results;
|
|
115
130
|
}
|
|
116
131
|
|
|
117
|
-
const isFinished = nextStep === null;
|
|
118
|
-
|
|
119
132
|
// Handle single next step
|
|
120
133
|
StepsEngine.getInstance().emit('stepStarted', { workflowName: stepsName, step: nextStep, state: newState, instanceId });
|
|
121
134
|
const connection = await Datastore.open();
|
|
@@ -124,29 +137,20 @@ class StepsEngine extends EventEmitter {
|
|
|
124
137
|
// Update the existing steps state in the database
|
|
125
138
|
newState = await connection.updateOne(StepsEngine.collectionName,
|
|
126
139
|
{ _id: instanceId },
|
|
127
|
-
{ $set: { ...newState, nextStep: nextStep, updatedAt: new Date().toISOString()
|
|
140
|
+
{ $set: { ...newState, nextStep: nextStep, updatedAt: new Date().toISOString() } });
|
|
128
141
|
|
|
129
142
|
StepsEngine.getInstance().emit('stateUpdated', { workflowName: stepsName, state: newState, instanceId });
|
|
130
143
|
|
|
144
|
+
|
|
145
|
+
|
|
131
146
|
// Get the next step function
|
|
132
147
|
const func = StepsEngine.getInstance().getDefinition(stepsName, nextStep);
|
|
133
148
|
|
|
134
|
-
// Check if the step is waiting for input
|
|
135
|
-
if (options && options.waitForInput === true) {
|
|
136
|
-
console.log('Steps workflow is paused, waiting for input for at step', nextStep);
|
|
137
|
-
this.emit('stepWaiting', { workflowName: stepsName, step: nextStep, instanceId });
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
149
|
try {
|
|
142
150
|
// Call the next step function with the step context
|
|
143
151
|
func.call(this, {...newState, instanceId: newState._id}, async function (nextStep, userState, options) {
|
|
144
152
|
try {
|
|
145
|
-
|
|
146
|
-
console.log('No next step, steps workflow completed');
|
|
147
|
-
StepsEngine.getInstance().emit('completed', { message: 'Steps workflow completed', ...userState });
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
153
|
+
|
|
150
154
|
// Protect system-level properties
|
|
151
155
|
const protectedState = {
|
|
152
156
|
_id: newState._id,
|
|
@@ -154,18 +158,32 @@ class StepsEngine extends EventEmitter {
|
|
|
154
158
|
createdAt: newState.createdAt,
|
|
155
159
|
updatedAt: newState.updatedAt,
|
|
156
160
|
instanceId: newState.instanceId,
|
|
157
|
-
|
|
161
|
+
workflowName: newState.workflowName
|
|
158
162
|
};
|
|
159
163
|
|
|
160
164
|
// Merge states with userState taking precedence, but protecting system fields
|
|
161
165
|
const mergedState = {
|
|
162
|
-
|
|
166
|
+
//...newState,
|
|
163
167
|
...userState,
|
|
164
168
|
...protectedState
|
|
165
169
|
};
|
|
166
170
|
|
|
171
|
+
// If there is no next step, the workflow is completed
|
|
172
|
+
if (nextStep === null) {
|
|
173
|
+
delete mergedState._id;
|
|
174
|
+
const completionTime = (new Date(mergedState.updatedAt) - new Date(mergedState.createdAt)) / 1000;
|
|
175
|
+
console.log(`Workflow ${stepsName} ${instanceId} is completed in time: ${completionTime}s 🎉`);
|
|
176
|
+
const db = await Datastore.open();
|
|
177
|
+
await db.updateOne(StepsEngine.collectionName,
|
|
178
|
+
{ _id: instanceId },
|
|
179
|
+
{ $set: { ...mergedState, nextStep: null, updatedAt: new Date().toISOString() } });
|
|
180
|
+
StepsEngine.getInstance().emit('completed', { message: 'Steps workflow completed', ...userState });
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
167
184
|
// Enqueue the next step
|
|
168
|
-
|
|
185
|
+
//console.log('enqueuing step', `${StepsEngine.queuePrefix}_${stepsName}_${nextStep}`);
|
|
186
|
+
connection.enqueue(`${StepsEngine.queuePrefix}_${stepsName}_${nextStep}`, {
|
|
169
187
|
stepsName: stepsName,
|
|
170
188
|
goto: nextStep,
|
|
171
189
|
state: mergedState,
|
|
@@ -180,7 +198,7 @@ class StepsEngine extends EventEmitter {
|
|
|
180
198
|
}
|
|
181
199
|
});
|
|
182
200
|
} catch (error) {
|
|
183
|
-
console.error('Error in step function:', error.message);
|
|
201
|
+
console.error('Error in step function: '+nextStep, error.message);
|
|
184
202
|
const connection = await Datastore.open();
|
|
185
203
|
await connection.updateOne(StepsEngine.collectionName,
|
|
186
204
|
{ _id: instanceId },
|
|
@@ -208,13 +226,14 @@ class StepsEngine extends EventEmitter {
|
|
|
208
226
|
// Validate each step in the definition
|
|
209
227
|
for (const [stepName, step] of Object.entries(definition)) {
|
|
210
228
|
try {
|
|
211
|
-
if (stepName !==
|
|
212
|
-
|
|
229
|
+
if (stepName !== undefined) {
|
|
230
|
+
//console.log('registering queue for step', `${StepsEngine.queuePrefix}_${name}_${stepName}`);
|
|
231
|
+
app.worker(`${StepsEngine.queuePrefix}_${name}_${stepName}`, async (req, res) => {
|
|
213
232
|
const { stepsName, goto, state, instanceId, options } = req.body.payload;
|
|
214
233
|
try {
|
|
215
234
|
const qid = await StepsEngine.getInstance().handleNextStep(stepsName, goto, state, instanceId, options);
|
|
216
235
|
} catch (error) {
|
|
217
|
-
console.error('Error in step function:', error.message);
|
|
236
|
+
console.error('Error in step function: ' + stepName, error.message);
|
|
218
237
|
} finally {
|
|
219
238
|
res.end();
|
|
220
239
|
}
|
|
@@ -252,8 +271,8 @@ class StepsEngine extends EventEmitter {
|
|
|
252
271
|
const connection = await Datastore.open();
|
|
253
272
|
// Create a new steps state in the database
|
|
254
273
|
const newState = await connection.insertOne(StepsEngine.collectionName,
|
|
255
|
-
{ ...initialState, nextStep: firstStepName, createdAt: new Date().toISOString() });
|
|
256
|
-
const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}`, {
|
|
274
|
+
{ ...initialState, nextStep: firstStepName, createdAt: new Date().toISOString(), workflowName: name });
|
|
275
|
+
const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}_${name}_${firstStepName}`, {
|
|
257
276
|
stepsName: name,
|
|
258
277
|
goto: firstStepName,
|
|
259
278
|
state: newState,
|
|
@@ -319,7 +338,7 @@ class StepsEngine extends EventEmitter {
|
|
|
319
338
|
StepsEngine.getInstance().emit('workflowContinued', { stepsName, step: state.nextStep, instanceId });
|
|
320
339
|
|
|
321
340
|
return new Promise(async (resolve, reject) => {
|
|
322
|
-
const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}`, {
|
|
341
|
+
const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}_${stepsName}_${state.nextStep}`, {
|
|
323
342
|
stepsName,
|
|
324
343
|
goto: state.nextStep,
|
|
325
344
|
state: state,
|