codehooks-js 1.3.3 → 1.3.5

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 (2) hide show
  1. package/package.json +1 -1
  2. package/workflow/engine.mjs +67 -50
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codehooks-js",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "type": "module",
5
5
  "description": "Codehooks.io official library - provides express.JS like syntax",
6
6
  "main": "index.js",
@@ -141,54 +141,68 @@ class StepsEngine extends EventEmitter {
141
141
 
142
142
  StepsEngine.getInstance().emit('stateUpdated', { workflowName: stepsName, state: newState, instanceId });
143
143
 
144
- // If there is no next step, the workflow is completed
145
- if (nextStep === null) {
146
- console.log('No next step, steps workflow completed');
147
- StepsEngine.getInstance().emit('completed', { message: 'Steps workflow completed', ...newState });
148
- return;
149
- }
150
144
 
151
- // Get the next step function
152
- const func = StepsEngine.getInstance().getDefinition(stepsName, nextStep);
153
-
145
+
146
+
154
147
  try {
155
- // Call the next step function with the step context
156
- func.call(this, {...newState, instanceId: newState._id}, async function (nextStep, userState, options) {
157
- try {
158
-
159
- // Protect system-level properties
160
- const protectedState = {
161
- _id: newState._id,
162
- nextStep: newState.nextStep,
163
- createdAt: newState.createdAt,
164
- updatedAt: newState.updatedAt,
165
- instanceId: newState.instanceId
166
- };
167
-
168
- // Merge states with userState taking precedence, but protecting system fields
169
- const mergedState = {
170
- //...newState,
171
- ...userState,
172
- ...protectedState
173
- };
148
+ // Get the next step function
149
+ const func = StepsEngine.getInstance().getDefinition(stepsName, nextStep);
150
+
151
+ // Wrap the callback in a Promise to ensure proper async handling
152
+ await new Promise((resolve, reject) => {
153
+ func({...newState, instanceId: newState._id}, async (nextStep, userState, options) => {
154
+ try {
155
+ // Protect system-level properties
156
+ const protectedState = {
157
+ _id: newState._id,
158
+ nextStep: newState.nextStep,
159
+ createdAt: newState.createdAt,
160
+ updatedAt: newState.updatedAt,
161
+ instanceId: newState.instanceId,
162
+ workflowName: newState.workflowName
163
+ };
164
+
165
+ // Merge states with userState taking precedence, but protecting system fields
166
+ const mergedState = {
167
+ ...userState,
168
+ ...protectedState
169
+ };
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
+
176
+ const finalresult = await connection.updateOne(StepsEngine.collectionName,
177
+ { _id: instanceId },
178
+ { $set: { ...mergedState, nextStep: null, updatedAt: new Date().toISOString() } });
179
+ console.log(`Workflow ${stepsName} ${instanceId} is completed in time: ${completionTime}s 🎉`);
180
+ StepsEngine.getInstance().emit('completed', { ...finalresult});
181
+ resolve();
182
+ return;
183
+ }
174
184
 
175
- // Enqueue the next step
176
- const qres = connection.enqueue(`${StepsEngine.queuePrefix}_${stepsName}`, {
177
- stepsName: stepsName,
178
- goto: nextStep,
179
- state: mergedState,
180
- options: options,
181
- instanceId: instanceId
182
- });
183
-
184
- StepsEngine.getInstance().emit('stepEnqueued', { workflowName: stepsName, step: nextStep, state: newState, instanceId });
185
- } catch (error) {
186
- console.log('error', error.message);
187
- throw error;
188
- }
185
+ // Enqueue the next step
186
+ console.debug('enqueuing step', nextStep, instanceId);
187
+ await connection.enqueue(`${StepsEngine.queuePrefix}_${stepsName}_${nextStep}`, {
188
+ stepsName: stepsName,
189
+ goto: nextStep,
190
+ state: mergedState,
191
+ options: options,
192
+ instanceId: instanceId
193
+ });
194
+
195
+ StepsEngine.getInstance().emit('stepEnqueued', { workflowName: stepsName, step: nextStep, state: newState, instanceId });
196
+ resolve();
197
+ } catch (error) {
198
+ console.error('error', error.message);
199
+ reject(error);
200
+ }
201
+ });
189
202
  });
190
203
  } catch (error) {
191
204
  console.error('Error in step function: '+nextStep, error.message);
205
+ StepsEngine.getInstance().emit('error', { workflowName: stepsName, step: nextStep, state: newState, instanceId, error: error.message });
192
206
  const connection = await Datastore.open();
193
207
  await connection.updateOne(StepsEngine.collectionName,
194
208
  { _id: instanceId },
@@ -216,10 +230,13 @@ class StepsEngine extends EventEmitter {
216
230
  // Validate each step in the definition
217
231
  for (const [stepName, step] of Object.entries(definition)) {
218
232
  try {
219
- if (stepName !== null) {
220
- app.worker(`${StepsEngine.queuePrefix}_${name}`, async (req, res) => {
221
- const { stepsName, goto, state, instanceId, options } = req.body.payload;
233
+ if (stepName !== undefined) {
234
+ //console.log('registering queue for step', `${StepsEngine.queuePrefix}_${name}_${stepName}`);
235
+ app.worker(`${StepsEngine.queuePrefix}_${name}_${stepName}`, async function(req, res) {
222
236
  try {
237
+
238
+ const { stepsName, goto, state, instanceId, options } = req.body.payload;
239
+ console.debug('worker job', stepName, instanceId);
223
240
  const qid = await StepsEngine.getInstance().handleNextStep(stepsName, goto, state, instanceId, options);
224
241
  } catch (error) {
225
242
  console.error('Error in step function: ' + stepName, error.message);
@@ -260,8 +277,8 @@ class StepsEngine extends EventEmitter {
260
277
  const connection = await Datastore.open();
261
278
  // Create a new steps state in the database
262
279
  const newState = await connection.insertOne(StepsEngine.collectionName,
263
- { ...initialState, nextStep: firstStepName, createdAt: new Date().toISOString() });
264
- const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}_${name}`, {
280
+ { ...initialState, nextStep: firstStepName, createdAt: new Date().toISOString(), workflowName: name });
281
+ const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}_${name}_${firstStepName}`, {
265
282
  stepsName: name,
266
283
  goto: firstStepName,
267
284
  state: newState,
@@ -323,11 +340,11 @@ class StepsEngine extends EventEmitter {
323
340
  throw new Error(`No steps found with instanceId: ${instanceId}`);
324
341
  }
325
342
 
326
- console.log('continue state', state);
343
+ console.debug('continue state', state);
327
344
  StepsEngine.getInstance().emit('workflowContinued', { stepsName, step: state.nextStep, instanceId });
328
345
 
329
346
  return new Promise(async (resolve, reject) => {
330
- const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}`, {
347
+ const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}_${stepsName}_${state.nextStep}`, {
331
348
  stepsName,
332
349
  goto: state.nextStep,
333
350
  state: state,
@@ -361,7 +378,7 @@ class StepsEngine extends EventEmitter {
361
378
  return new Promise(async (resolve, reject) => {
362
379
  const connection = await Datastore.open();
363
380
  const states = await connection.find(StepsEngine.collectionName, filter).toArray();
364
- console.log('listSteps', StepsEngine.collectionName, filter, states.length);
381
+ console.debug('listSteps', StepsEngine.collectionName, filter, states.length);
365
382
  resolve(states);
366
383
  });
367
384
  }