codehooks-js 1.3.15 → 1.3.17

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codehooks-js",
3
- "version": "1.3.15",
3
+ "version": "1.3.17",
4
4
  "type": "module",
5
5
  "description": "Codehooks.io official library - provides express.JS like syntax",
6
6
  "main": "index.js",
package/types/index.d.ts CHANGED
@@ -513,6 +513,10 @@ export type httpRequest = {
513
513
  * - JSON payload
514
514
  */
515
515
  body: any;
516
+ /**
517
+ * - Get the raw unparsed request body as a string
518
+ */
519
+ rawBody: string;
516
520
  /**
517
521
  * - Get the URL full path, e.g. /dev/myroute
518
522
  */
@@ -622,6 +626,11 @@ export type httpResponse = {
622
626
 
623
627
  export type nextFunction = (error?: string) => void;
624
628
 
629
+ // Aliases for developers familiar with Express.js conventions
630
+ export type Request = httpRequest;
631
+ export type Response = httpResponse;
632
+ export type NextFunction = nextFunction;
633
+
625
634
  export type Filesystem = {
626
635
  /**
627
636
  * - Get binary file input stream. Takes a path (string) to file (e.g. /static/logo.png) and an options object (Object) for future use. Returns a Promise resolving to a binary data stream emitter.
@@ -1309,6 +1318,27 @@ export type Workflow = {
1309
1318
  */
1310
1319
  cancelSteps: (id: string) => Promise<any>;
1311
1320
 
1321
+ /**
1322
+ * Get the status of a workflow instance (alias for getStepsStatus)
1323
+ * @param id - ID of the workflow instance
1324
+ * @returns Promise with the workflow status
1325
+ */
1326
+ getWorkflowStatus: (id: string) => Promise<any>;
1327
+
1328
+ /**
1329
+ * Get the state of a workflow instance (alias for getStepsStatus)
1330
+ * @param id - ID of the workflow instance
1331
+ * @returns Promise with the workflow state
1332
+ */
1333
+ getState: (id: string) => Promise<any>;
1334
+
1335
+ /**
1336
+ * Cancel a workflow instance (alias for cancelSteps)
1337
+ * @param id - ID of the workflow instance to cancel
1338
+ * @returns Promise with the cancellation result
1339
+ */
1340
+ cancelWorkflow: (id: string) => Promise<any>;
1341
+
1312
1342
  /**
1313
1343
  * Register an event listener
1314
1344
  * @param event - Name of the event to listen for
@@ -117,5 +117,23 @@ declare class StepsEngine {
117
117
  * @returns {Promise<Object>} The cancellation result
118
118
  */
119
119
  cancelSteps(id: string): Promise<any>;
120
+ /**
121
+ * Get the status of a workflow instance (alias for getStepsStatus)
122
+ * @param {string} id - ID of the workflow instance
123
+ * @returns {Promise<Object>} The workflow status
124
+ */
125
+ getWorkflowStatus(id: string): Promise<any>;
126
+ /**
127
+ * Get the state of a workflow instance (alias for getStepsStatus)
128
+ * @param {string} id - ID of the workflow instance
129
+ * @returns {Promise<Object>} The workflow state
130
+ */
131
+ getState(id: string): Promise<any>;
132
+ /**
133
+ * Cancel a workflow instance (alias for cancelSteps)
134
+ * @param {string} id - ID of the workflow instance to cancel
135
+ * @returns {Promise<Object>} The cancellation result
136
+ */
137
+ cancelWorkflow(id: string): Promise<any>;
120
138
  }
121
139
  //# sourceMappingURL=engine.d.mts.map
@@ -38,7 +38,7 @@ class Workflow extends EventEmitter {
38
38
  this.#definitions = new Map();
39
39
  this.#name = name;
40
40
  this.#description = description;
41
-
41
+
42
42
  // Apply any configuration options
43
43
  if (options) {
44
44
  this.configure(options);
@@ -120,7 +120,7 @@ class Workflow extends EventEmitter {
120
120
  * @throws {Error} If maxStepCount is not a positive number
121
121
  */
122
122
  setMaxStepCount(maxStepCount) {
123
- if (typeof maxStepCount !== 'number' || maxStepCount <= 0) {
123
+ if (typeof maxStepCount !== 'number' || maxStepCount <= 0) {
124
124
  throw new Error('Maximum step count must be a positive number');
125
125
  }
126
126
  this.#maxStepCount = maxStepCount;
@@ -226,13 +226,13 @@ class Workflow extends EventEmitter {
226
226
  * @throws {Error} If step execution fails
227
227
  */
228
228
  async handleNextStep(stepsName, nextStep, newState, instanceId, options) {
229
-
229
+
230
230
  // open the connection to the database
231
- const connection = await DB.open();
231
+ const connection = await DB.open();
232
232
 
233
233
  // Handle single next step
234
234
  this.emit('stepStarted', { workflowName: stepsName, step: nextStep, state: newState, instanceId });
235
-
235
+
236
236
  // remove the _id from the newState
237
237
  delete newState._id;
238
238
  // increment the step count
@@ -247,9 +247,9 @@ class Workflow extends EventEmitter {
247
247
  newState = await connection.updateOne(this.#collectionName,
248
248
  { _id: instanceId },
249
249
  { $set: { ...newState, nextStep: nextStep, updatedAt: new Date().toISOString(), stepCount: newState.stepCount } });
250
-
251
- this.emit('stateUpdated', { workflowName: stepsName, state: newState, instanceId });
252
-
250
+
251
+ this.emit('stateUpdated', { workflowName: stepsName, state: newState, instanceId });
252
+
253
253
  try {
254
254
  // Get the next step function
255
255
  const func = this.getDefinition(stepsName, nextStep);
@@ -267,14 +267,14 @@ class Workflow extends EventEmitter {
267
267
  console.debug('waiter', state);
268
268
  await connection.updateOne(this.#collectionName,
269
269
  { _id: instanceId },
270
- { $set: { ...state, updatedAt: new Date().toISOString() } });
270
+ { $set: { ...state, updatedAt: new Date().toISOString() } });
271
271
  resolve();
272
272
  return;
273
273
  } else {
274
274
  resolve(); // no state update
275
275
  }
276
276
  }
277
- await func({...newState, instanceId: newState._id}, async (nextStep, userState, options) => {
277
+ await func({ ...newState, instanceId: newState._id }, async (nextStep, userState, options) => {
278
278
  try {
279
279
  // Protect system-level properties
280
280
  const protectedState = {
@@ -288,7 +288,7 @@ class Workflow extends EventEmitter {
288
288
  parallelSteps: newState.parallelSteps,
289
289
  previousStep: newState.nextStep
290
290
  };
291
-
291
+
292
292
  // Merge states with userState taking precedence, but protecting system fields
293
293
  const mergedState = {
294
294
  ...userState,
@@ -302,14 +302,14 @@ class Workflow extends EventEmitter {
302
302
  // update the parallel steps metadata
303
303
  if (mergedState.parallelSteps && mergedState.parallelSteps[mergedState.nextStep]) {
304
304
  // get a fresh copy of the parallel steps
305
- const fresh = await connection.findOne(this.#collectionName, { _id: instanceId });
305
+ const fresh = await connection.findOne(this.#collectionName, { _id: instanceId });
306
306
  fresh.parallelSteps[mergedState.nextStep].done = true;
307
307
  fresh.parallelSteps[mergedState.nextStep].nextStep = nextStep;
308
308
  //fresh.parallelSteps[mergedState.nextStep].previousStep = mergedState.previousStep || null;
309
309
  delete fresh._id;
310
310
  const updated = await connection.updateOne(this.#collectionName,
311
311
  { _id: instanceId },
312
- { $set: { ...fresh, parallelSteps: fresh.parallelSteps } });
312
+ { $set: { ...fresh, parallelSteps: fresh.parallelSteps } });
313
313
  //console.debug('updated', updated.parallelSteps);
314
314
  mergedState.parallelSteps = fresh.parallelSteps;
315
315
  // Check if all parallel steps are done
@@ -335,12 +335,12 @@ class Workflow extends EventEmitter {
335
335
  if (nextStep === null) {
336
336
  delete mergedState._id;
337
337
  const completionTime = (new Date(mergedState.updatedAt) - new Date(mergedState.createdAt));
338
-
338
+
339
339
  const finalresult = await connection.updateOne(this.#collectionName,
340
340
  { _id: instanceId },
341
- { $set: { ...mergedState, nextStep: null, updatedAt: new Date().toISOString(), totalTime: completionTime } });
341
+ { $set: { ...mergedState, nextStep: null, updatedAt: new Date().toISOString(), totalTime: completionTime } });
342
342
  console.log(`Workflow ${stepsName} ${instanceId} is completed in time: ${completionTime / 1000}s 🎉`);
343
- this.emit('completed', { ...finalresult});
343
+ this.emit('completed', { ...finalresult });
344
344
  resolve();
345
345
  return;
346
346
  }
@@ -370,13 +370,13 @@ class Workflow extends EventEmitter {
370
370
  } else {
371
371
  console.debug('enqueue step', nextStep, instanceId);
372
372
  await connection.enqueue(`${this.#queuePrefix}_${stepsName}_${nextStep}`, {
373
- stepsName: stepsName,
374
- goto: nextStep,
375
- state: mergedState,
376
- options: options,
377
- instanceId: instanceId
378
- });
379
- }
373
+ stepsName: stepsName,
374
+ goto: nextStep,
375
+ state: mergedState,
376
+ options: options,
377
+ instanceId: instanceId
378
+ });
379
+ }
380
380
  this.emit('stepEnqueued', { workflowName: stepsName, step: nextStep, state: newState, instanceId });
381
381
  resolve();
382
382
  } catch (error) {
@@ -393,7 +393,7 @@ class Workflow extends EventEmitter {
393
393
  } catch (error) {
394
394
  console.error('error in handleNextStep outer', error.message);
395
395
  throw error;
396
- }
396
+ }
397
397
  }
398
398
 
399
399
  /**
@@ -424,15 +424,15 @@ class Workflow extends EventEmitter {
424
424
  * @private
425
425
  */
426
426
  async registerWithApp(app, name, description, definition) {
427
- this.emit('workflowCreated', { name, description });
427
+ this.emit('workflowCreated', { name, description });
428
428
 
429
429
  // Validate each step in the definition
430
430
  for (const [stepName, step] of Object.entries(definition)) {
431
431
  try {
432
432
  if (stepName !== undefined) {
433
433
  console.debug('registering queue for step', `${this.#queuePrefix}_${name}_${stepName}`);
434
- app.worker(`${this.#queuePrefix}_${name}_${stepName}`, async (req, res) => {
435
- try {
434
+ app.worker(`${this.#queuePrefix}_${name}_${stepName}`, async (req, res) => {
435
+ try {
436
436
  const { stepsName, goto, state, instanceId, options } = req.body.payload;
437
437
  console.debug('dequeue step', stepName, instanceId);
438
438
  const qid = await this.handleNextStep(stepsName, goto, state, instanceId, options);
@@ -460,7 +460,7 @@ class Workflow extends EventEmitter {
460
460
  }
461
461
 
462
462
  // Store the definition
463
- this.#definitions.set(name, definition);
463
+ this.#definitions.set(name, definition);
464
464
 
465
465
  return name;
466
466
  }
@@ -472,13 +472,13 @@ class Workflow extends EventEmitter {
472
472
  * @throws {Error} If starting steps fails
473
473
  */
474
474
  async start(initialState) {
475
- this.emit('workflowStarted', { name: this.#name, initialState });
475
+ this.emit('workflowStarted', { name: this.#name, initialState });
476
476
 
477
477
  return new Promise(async (resolve, reject) => {
478
478
  try {
479
479
  console.debug('Starting workflow', this.#name);
480
480
  const funcs = this.#definitions.get(this.#name);
481
-
481
+
482
482
  if (!funcs) {
483
483
  reject(new Error(`No workflow definition found for: ${this.#name}`));
484
484
  return;
@@ -486,7 +486,7 @@ class Workflow extends EventEmitter {
486
486
 
487
487
  const firstStepName = Object.keys(funcs)[0];
488
488
  const firstStep = funcs[firstStepName];
489
-
489
+
490
490
  if (!firstStep) {
491
491
  reject(new Error('No start step defined in workflow'));
492
492
  return;
@@ -495,7 +495,7 @@ class Workflow extends EventEmitter {
495
495
  const connection = await DB.open();
496
496
  // Create a new workflow state in the database
497
497
  const newState = await connection.insertOne(this.#collectionName,
498
- { ...initialState, nextStep: firstStepName, createdAt: new Date().toISOString(), workflowName: this.#name, stepCount: { } });
498
+ { ...initialState, nextStep: firstStepName, createdAt: new Date().toISOString(), workflowName: this.#name, stepCount: {} });
499
499
  const { _id: ID } = await connection.enqueue(`${this.#queuePrefix}_${this.#name}_${firstStepName}`, {
500
500
  stepsName: this.#name,
501
501
  goto: firstStepName,
@@ -518,17 +518,17 @@ class Workflow extends EventEmitter {
518
518
  * @param {Object} options - Options for the update, { continue: false } to avoid continuing the the step
519
519
  * @returns {Promise<Object>} The updated state
520
520
  */
521
- async updateState(instanceId, state, options={continue: true}) {
521
+ async updateState(instanceId, state, options = { continue: true }) {
522
522
  this.emit('stepsStateUpdating', { name: this.#name, instanceId, state });
523
523
  const connection = await DB.open();
524
524
  return new Promise(async (resolve, reject) => {
525
- const doc = await connection.updateOne(this.#collectionName,
526
- { _id: instanceId },
525
+ const doc = await connection.updateOne(this.#collectionName,
526
+ { _id: instanceId },
527
527
  { $set: { ...state, updatedAt: new Date().toISOString() } });
528
528
  if (options.continue) {
529
529
  await this.continue(instanceId, false);
530
- }
531
- resolve({ ...doc });
530
+ }
531
+ resolve({ ...doc });
532
532
  });
533
533
  }
534
534
 
@@ -549,7 +549,7 @@ class Workflow extends EventEmitter {
549
549
  * @returns {Promise<{qId: string}>} Queue ID for the continued step
550
550
  * @throws {Error} If steps instance not found
551
551
  */
552
- async continue(instanceId, reset=false) {
552
+ async continue(instanceId, reset = false) {
553
553
  const connection = await DB.open();
554
554
  const state = await connection.findOne(this.#collectionName, { _id: instanceId });
555
555
  if (!state) {
@@ -565,10 +565,10 @@ class Workflow extends EventEmitter {
565
565
  // update the step count
566
566
  state.stepCount[state.nextStep] = { visits: 0, startTime: new Date().toISOString() };
567
567
  }
568
-
568
+
569
569
  await connection.updateOne(this.#collectionName, { _id: instanceId }, { $set: { stepCount: state.stepCount } });
570
570
  console.debug('continue state', state);
571
- this.emit('workflowContinued', { name: this.#name, step: state.nextStep, instanceId });
571
+ this.emit('workflowContinued', { name: this.#name, step: state.nextStep, instanceId });
572
572
 
573
573
  return new Promise(async (resolve, reject) => {
574
574
  const { _id: ID } = await connection.enqueue(`${this.#queuePrefix}_${this.#name}_${state.nextStep}`, {
@@ -589,7 +589,7 @@ class Workflow extends EventEmitter {
589
589
  */
590
590
  async continueAllTimedOut() {
591
591
  const db = await DB.open();
592
- const timedOutWorkflows = await db.collection(this.#collectionName).find({nextStep: {$ne: null}}).toArray();
592
+ const timedOutWorkflows = await db.collection(this.#collectionName).find({ nextStep: { $ne: null } }).toArray();
593
593
  const now = new Date();
594
594
  const results = [];
595
595
  for (const workflow of timedOutWorkflows) {
@@ -598,7 +598,7 @@ class Workflow extends EventEmitter {
598
598
  if (diffMillis > this.#timeout) {
599
599
  const diffMinutes = diffMillis / (1000 * 60);
600
600
  console.log('Timed out:', workflow._id, workflow.nextStep, `(${diffMinutes.toFixed(1)} minutes old)`);
601
- const result = await this.continue(workflow._id, true);
601
+ const result = await this.continue(workflow._id, true);
602
602
  console.log('Continued:', result._id);
603
603
  results.push(result);
604
604
  }
@@ -642,13 +642,40 @@ class Workflow extends EventEmitter {
642
642
  this.emit('cancelled', { id });
643
643
  return new Promise(async (resolve, reject) => {
644
644
  const connection = await DB.open();
645
- const state = await connection.updateOne(this.#collectionName,
646
- { _id: id },
647
- { $set: { status: 'cancelled' } });
645
+ const state = await connection.updateOne(this.#collectionName,
646
+ { _id: id },
647
+ { $set: { status: 'cancelled' } });
648
648
  resolve(state);
649
649
  });
650
650
  }
651
651
 
652
+ /**
653
+ * Get the status of a workflow instance (alias for getStepsStatus)
654
+ * @param {string} id - ID of the workflow instance
655
+ * @returns {Promise<Object>} The workflow status
656
+ */
657
+ async getWorkflowStatus(id) {
658
+ return this.getStepsStatus(id);
659
+ }
660
+
661
+ /**
662
+ * Get the state of a workflow instance (alias for getStepsStatus)
663
+ * @param {string} id - ID of the workflow instance
664
+ * @returns {Promise<Object>} The workflow state
665
+ */
666
+ async getState(id) {
667
+ return this.getStepsStatus(id);
668
+ }
669
+
670
+ /**
671
+ * Cancel a workflow instance (alias for cancelSteps)
672
+ * @param {string} id - ID of the workflow instance to cancel
673
+ * @returns {Promise<Object>} The cancellation result
674
+ */
675
+ async cancelWorkflow(id) {
676
+ return this.cancelSteps(id);
677
+ }
678
+
652
679
  /**
653
680
  * Check if a specific step in a workflow instance has timed out
654
681
  * @param {Object} workflow - The workflow instance
@@ -658,7 +685,7 @@ class Workflow extends EventEmitter {
658
685
  isStepTimedOut(workflow) {
659
686
  // Use previousStep if no stepName provided
660
687
  const stepToCheck = workflow.nextStep;
661
-
688
+
662
689
  if (!stepToCheck || !workflow.stepCount || !workflow.stepCount[stepToCheck]) {
663
690
  console.debug('no step', stepToCheck, workflow.stepCount);
664
691
  return {
@@ -669,12 +696,12 @@ class Workflow extends EventEmitter {
669
696
  }
670
697
 
671
698
  const step = workflow.stepCount[stepToCheck];
672
-
699
+
673
700
  // Get the timeout value for this step
674
701
  // First try step-specific config, then default options, finally fallback to global timeout
675
702
  const stepConfig = this.#steps[stepToCheck];
676
703
  const stepTimeout = stepConfig?.timeout ?? this.#defaultStepOptions.timeout ?? this.#timeout;
677
-
704
+
678
705
  // If the step hasn't finished, check if it's been running too long
679
706
  console.debug('isStepTimedOut', stepToCheck, stepTimeout);
680
707
  if (!step.finishTime) {
@@ -716,7 +743,7 @@ class Workflow extends EventEmitter {
716
743
  */
717
744
  async findTimedOutSteps(filter = {}) {
718
745
  const db = await DB.open();
719
- const workflows = await db.getMany(this.#collectionName, {"nextStep": {$ne: null}}).toArray();
746
+ const workflows = await db.getMany(this.#collectionName, { "nextStep": { $ne: null } }).toArray();
720
747
  if (workflows.length > 0) {
721
748
  console.debug('TimedOutSteps', workflows.length);
722
749
  }