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 +8 -3
- package/package.json +1 -1
- package/types/index.d.ts +25 -18
- package/workflow/engine.mjs +53 -6
package/index.js
CHANGED
|
@@ -76,9 +76,14 @@ class Codehooks {
|
|
|
76
76
|
this.queues[topic] = hook;
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
-
worker = (name,
|
|
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
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 {
|
|
895
|
-
* @param
|
|
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
|
-
*
|
|
898
|
-
*
|
|
899
|
-
*
|
|
900
|
-
*
|
|
901
|
-
*
|
|
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
|
-
*
|
|
905
|
-
*
|
|
906
|
-
*
|
|
907
|
-
*
|
|
908
|
-
*
|
|
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
|
|
913
|
-
|
|
918
|
+
name: string,
|
|
919
|
+
hook: (
|
|
914
920
|
request: httpRequest,
|
|
915
921
|
response: httpResponse,
|
|
916
|
-
next
|
|
917
|
-
) => any
|
|
918
|
-
|
|
922
|
+
next?: nextFunction
|
|
923
|
+
) => any,
|
|
924
|
+
options?: Record<string, any>
|
|
925
|
+
) => Promise<void>;
|
|
919
926
|
/**
|
|
920
927
|
* Create cron background jobs
|
|
921
928
|
* @example
|
package/workflow/engine.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
});
|