codehooks-js 1.3.19 → 1.3.21

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/index.js CHANGED
@@ -76,9 +76,14 @@ class Codehooks {
76
76
  this.queues[topic] = hook;
77
77
  };
78
78
 
79
- worker = (name, ...hook) => {
80
- this.workers[name] = hook;
81
- this.queues[name] = hook; // legacy support
79
+ worker = async (name, hook, options={}) => {
80
+ this.workers[name] = [hook];
81
+ this.queues[name] = [hook]; // legacy support
82
+
83
+ // add option to key-value store if options is not empty
84
+ if (Object.keys(options).length > 0) {
85
+ this.set(`queue_worker:${name}`, options);
86
+ }
82
87
  };
83
88
 
84
89
  job = (cronExpression, ...hook) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codehooks-js",
3
- "version": "1.3.19",
3
+ "version": "1.3.21",
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
@@ -891,31 +891,38 @@ export class Codehooks {
891
891
  ) => void;
892
892
  /**
893
893
  * Add application worker function
894
- * @param {object} name a unique worker name or JSON configuration
895
- * @param {...function(httpRequest, httpResponse, function(string):void)} hook - callback function(s) with parameters (req, res, [next])
894
+ * @param {string} name - Unique worker name
895
+ * @param {function} hook - Worker callback function with parameters (req, res, [next])
896
+ * @param {Record<string, any>} options - Optional configuration stored in key-value store under `worker:${name}` key in keyspace '_codehooks_worker_settings'
897
+ *
896
898
  * @example
897
- * app.worker('myworker', (data, job) => {
898
- * const {body:{payload}} = data
899
- * //console.debug('worker payload data', payload)
900
- * // do stuff with payload data
901
- * job.end()
899
+ * // Simple worker
900
+ * await app.worker('myworker', (req, res) => {
901
+ * const {body:{payload}} = req
902
+ * console.log('worker payload data', payload)
903
+ * res.end()
902
904
  *})
905
+ *
903
906
  * @example
904
- * app.worker({name: 'myworker', workers: 5}, (data, job) => {
905
- * const {body:{payload}} = data
906
- * //console.debug('worker payload data', payload)
907
- * // do stuff with payload data
908
- * job.end()
909
- *})
907
+ * // Worker with configuration stored in KV store
908
+ * await app.worker('myworker', (req, res) => {
909
+ * const {body:{payload}} = req
910
+ * console.log('Processing:', payload)
911
+ * res.end()
912
+ * }, {
913
+ * workers: 2,
914
+ * timeout: 30000
915
+ * })
910
916
  */
911
917
  worker: (
912
- name: string | object,
913
- ...hook: ((
918
+ name: string,
919
+ hook: (
914
920
  request: httpRequest,
915
921
  response: httpResponse,
916
- next: nextFunction
917
- ) => any)[]
918
- ) => void;
922
+ next?: nextFunction
923
+ ) => any,
924
+ options?: Record<string, any>
925
+ ) => Promise<void>;
919
926
  /**
920
927
  * Create cron background jobs
921
928
  * @example
@@ -23,7 +23,8 @@ class Workflow extends EventEmitter {
23
23
  #description = null; // workflow description
24
24
  #defaultStepOptions = { // default step options
25
25
  timeout: 30000, // timeout for a step
26
- maxRetries: 3 // maximum number of retries for a step
26
+ maxRetries: 3, // maximum number of retries for a step
27
+ workers: 1 // number of workers for a step
27
28
  };
28
29
 
29
30
  /**
@@ -154,6 +155,36 @@ class Workflow extends EventEmitter {
154
155
  return this.#steps;
155
156
  }
156
157
 
158
+ /**
159
+ * Get the number of workers for a specific step
160
+ * @param {string} stepName - Name of the step
161
+ * @returns {number} The number of workers for the step
162
+ */
163
+ getWorkersForStep(stepName) {
164
+ const stepConfig = this.#steps[stepName];
165
+ return stepConfig?.workers ?? this.#defaultStepOptions.workers ?? 1;
166
+ }
167
+
168
+ /**
169
+ * Set the default number of workers for steps
170
+ * @param {number} workers - Number of workers
171
+ * @throws {Error} If workers is not a positive number
172
+ */
173
+ setDefaultWorkers(workers) {
174
+ if (typeof workers !== 'number' || workers <= 0) {
175
+ throw new Error('Workers must be a positive number');
176
+ }
177
+ this.#defaultStepOptions.workers = workers;
178
+ }
179
+
180
+ /**
181
+ * Get the default number of workers
182
+ * @returns {number} The default number of workers
183
+ */
184
+ getDefaultWorkers() {
185
+ return this.#defaultStepOptions.workers;
186
+ }
187
+
157
188
  /**
158
189
  * Configure the steps engine
159
190
  * @param {Object} config - Configuration object
@@ -161,6 +192,7 @@ class Workflow extends EventEmitter {
161
192
  * @param {string} config.queuePrefix - Queue prefix
162
193
  * @param {number} config.timeout - Timeout in milliseconds
163
194
  * @param {number} config.maxStepCount - Maximum step count
195
+ * @param {number} config.workers - Default number of workers for steps
164
196
  * @param {Object} config.steps - Steps configuration
165
197
  */
166
198
  configure(config) {
@@ -176,6 +208,9 @@ class Workflow extends EventEmitter {
176
208
  if (config.maxStepCount) {
177
209
  this.setMaxStepCount(config.maxStepCount);
178
210
  }
211
+ if (config.workers) {
212
+ this.setDefaultWorkers(config.workers);
213
+ }
179
214
  if (config.steps) {
180
215
  this.setStepsConfig(config.steps);
181
216
  }
@@ -345,6 +380,11 @@ class Workflow extends EventEmitter {
345
380
  return;
346
381
  }
347
382
 
383
+ // Additional safety check to prevent enqueueing null or invalid steps
384
+ if (nextStep === null || nextStep === undefined || nextStep === 'null') {
385
+ throw new Error(`Invalid nextStep value: ${nextStep}. This should have been caught earlier.`);
386
+ }
387
+
348
388
  // Enqueue the next step, single or parallel
349
389
  if (Array.isArray(nextStep)) {
350
390
  const now = new Date().toISOString();
@@ -365,7 +405,7 @@ class Workflow extends EventEmitter {
365
405
  state: mergedState,
366
406
  options: options,
367
407
  instanceId: instanceId
368
- });
408
+ }, { workers: this.getWorkersForStep(step) });
369
409
  }
370
410
  } else {
371
411
  console.debug('enqueue step', nextStep, instanceId);
@@ -375,7 +415,7 @@ class Workflow extends EventEmitter {
375
415
  state: mergedState,
376
416
  options: options,
377
417
  instanceId: instanceId
378
- });
418
+ }, { workers: this.getWorkersForStep(nextStep) });
379
419
  }
380
420
  this.emit('stepEnqueued', { workflowName: stepsName, step: nextStep, state: newState, instanceId });
381
421
  resolve();
@@ -429,7 +469,8 @@ class Workflow extends EventEmitter {
429
469
  // Validate each step in the definition
430
470
  for (const [stepName, step] of Object.entries(definition)) {
431
471
  try {
432
- if (stepName !== undefined) {
472
+ // Skip null, undefined, or invalid step names
473
+ if (stepName !== undefined && stepName !== null && stepName !== 'null' && stepName.trim() !== '') {
433
474
  console.debug('registering queue for step', `${this.#queuePrefix}_${name}_${stepName}`);
434
475
  app.worker(`${this.#queuePrefix}_${name}_${stepName}`, async (req, res) => {
435
476
  try {
@@ -502,7 +543,7 @@ class Workflow extends EventEmitter {
502
543
  state: newState,
503
544
  instanceId: newState._id,
504
545
  options: {}
505
- });
546
+ }, { workers: this.getWorkersForStep(firstStepName) });
506
547
  resolve(newState);
507
548
  } catch (error) {
508
549
  console.error('Error starting workflow:', error.message);
@@ -555,6 +596,12 @@ class Workflow extends EventEmitter {
555
596
  if (!state) {
556
597
  throw new Error(`No steps found with instanceId: ${instanceId}`);
557
598
  }
599
+
600
+ // Check if the workflow has already completed
601
+ if (state.nextStep === null) {
602
+ throw new Error(`Workflow ${instanceId} has already completed and cannot be continued`);
603
+ }
604
+
558
605
  if (reset) {
559
606
  // reset the step count
560
607
  // update all step counts to 0
@@ -577,7 +624,7 @@ class Workflow extends EventEmitter {
577
624
  state: state,
578
625
  options: {},
579
626
  instanceId: instanceId
580
- });
627
+ }, { workers: this.getWorkersForStep(state.nextStep) });
581
628
 
582
629
  resolve({ instanceId });
583
630
  });