codehooks-js 1.3.1 → 1.3.3
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 +7 -7
- package/package.json +1 -1
- package/types/index.d.ts +9 -5
- package/types/workflow/engine.d.mts +10 -0
- package/types/workflow/index.d.mts +4 -0
- package/workflow/engine.mjs +40 -32
- package/workflow/index.mjs +3 -0
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {agg} from './aggregation/index.mjs';
|
|
2
2
|
import {crudlify as crud} from './crudlify/index.mjs';
|
|
3
3
|
import {serveStatic as ws, render as renderView} from './webserver.mjs';
|
|
4
|
-
import {Steps, StepsConfig} from './workflow/index.mjs';
|
|
4
|
+
import {Steps as Workflow, StepsConfig as WorkflowConfig} from './workflow/index.mjs';
|
|
5
5
|
|
|
6
6
|
function createRoute(str) {
|
|
7
7
|
if(str instanceof RegExp) {
|
|
@@ -117,9 +117,9 @@ class Codehooks {
|
|
|
117
117
|
})
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return
|
|
120
|
+
createWorkflow = (name, description, steps, options={}) => {
|
|
121
|
+
Workflow.register(this, name, description, steps, options);
|
|
122
|
+
return Workflow;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
init = (hook) => {
|
|
@@ -193,10 +193,10 @@ export const aggregation = agg;
|
|
|
193
193
|
export const crudlify = crud;
|
|
194
194
|
export const coho = _coho;
|
|
195
195
|
export const app = _coho;
|
|
196
|
-
export const
|
|
196
|
+
export const workflowconfig = WorkflowConfig;
|
|
197
197
|
export {
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
Workflow,
|
|
199
|
+
WorkflowConfig
|
|
200
200
|
};
|
|
201
201
|
export const realtime = {
|
|
202
202
|
createChannel: (path, ...hook) => {
|
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export const aggregation: typeof agg;
|
|
|
4
4
|
export const crudlify: typeof crud;
|
|
5
5
|
export const coho: Codehooks;
|
|
6
6
|
export const app: Codehooks;
|
|
7
|
-
export const
|
|
7
|
+
export const workflowconfig: {
|
|
8
8
|
setCollectionName: any;
|
|
9
9
|
setQueuePrefix: any;
|
|
10
10
|
};
|
|
@@ -62,8 +62,12 @@ declare class Codehooks {
|
|
|
62
62
|
set: (key: any, val: any) => void;
|
|
63
63
|
render: (view: any, data: any, cb: any) => Promise<void>;
|
|
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[]>;
|
|
@@ -124,7 +128,7 @@ declare class Codehooks {
|
|
|
124
128
|
};
|
|
125
129
|
addListener: (observer: any) => void;
|
|
126
130
|
}
|
|
127
|
-
import { Steps } from './workflow/index.mjs';
|
|
128
|
-
import { StepsConfig } from './workflow/index.mjs';
|
|
131
|
+
import { Steps as Workflow } from './workflow/index.mjs';
|
|
132
|
+
import { StepsConfig as WorkflowConfig } from './workflow/index.mjs';
|
|
129
133
|
declare const _coho: Codehooks;
|
|
130
|
-
export {
|
|
134
|
+
export { Workflow, WorkflowConfig };
|
|
@@ -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,58 +129,51 @@ 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
|
-
StepsEngine.getInstance().emit('stepStarted', { stepsName, step: nextStep, state: newState, instanceId });
|
|
133
|
+
StepsEngine.getInstance().emit('stepStarted', { workflowName: stepsName, step: nextStep, state: newState, instanceId });
|
|
121
134
|
const connection = await Datastore.open();
|
|
122
135
|
// remove the _id from the newState
|
|
123
136
|
delete newState._id;
|
|
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
|
-
StepsEngine.getInstance().emit('stateUpdated', { stepsName, state: newState, instanceId });
|
|
142
|
+
StepsEngine.getInstance().emit('stateUpdated', { workflowName: stepsName, state: newState, instanceId });
|
|
130
143
|
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (options && options.waitForInput === true) {
|
|
136
|
-
console.log('Steps workflow is paused, waiting for input for at step', nextStep);
|
|
137
|
-
this.emit('stepWaiting', { stepsName, step: nextStep, instanceId });
|
|
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 });
|
|
138
148
|
return;
|
|
139
149
|
}
|
|
150
|
+
|
|
151
|
+
// Get the next step function
|
|
152
|
+
const func = StepsEngine.getInstance().getDefinition(stepsName, nextStep);
|
|
140
153
|
|
|
141
154
|
try {
|
|
142
155
|
// Call the next step function with the step context
|
|
143
156
|
func.call(this, {...newState, instanceId: newState._id}, async function (nextStep, userState, options) {
|
|
144
157
|
try {
|
|
145
|
-
|
|
146
|
-
console.log('No next step, steps workflow completed');
|
|
147
|
-
StepsEngine.getInstance().emit('completed', { message: 'Steps workflow completed', ...userState });
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
158
|
+
|
|
150
159
|
// Protect system-level properties
|
|
151
160
|
const protectedState = {
|
|
152
161
|
_id: newState._id,
|
|
153
162
|
nextStep: newState.nextStep,
|
|
154
163
|
createdAt: newState.createdAt,
|
|
155
164
|
updatedAt: newState.updatedAt,
|
|
156
|
-
instanceId: newState.instanceId
|
|
157
|
-
isFinished: newState.isFinished
|
|
165
|
+
instanceId: newState.instanceId
|
|
158
166
|
};
|
|
159
167
|
|
|
160
168
|
// Merge states with userState taking precedence, but protecting system fields
|
|
161
169
|
const mergedState = {
|
|
162
|
-
|
|
170
|
+
//...newState,
|
|
163
171
|
...userState,
|
|
164
172
|
...protectedState
|
|
165
173
|
};
|
|
166
174
|
|
|
167
175
|
// Enqueue the next step
|
|
168
|
-
const qres = connection.enqueue(`${StepsEngine.queuePrefix}`, {
|
|
176
|
+
const qres = connection.enqueue(`${StepsEngine.queuePrefix}_${stepsName}`, {
|
|
169
177
|
stepsName: stepsName,
|
|
170
178
|
goto: nextStep,
|
|
171
179
|
state: mergedState,
|
|
@@ -173,14 +181,14 @@ class StepsEngine extends EventEmitter {
|
|
|
173
181
|
instanceId: instanceId
|
|
174
182
|
});
|
|
175
183
|
|
|
176
|
-
StepsEngine.getInstance().emit('stepEnqueued', { stepsName, step: nextStep, state: newState, instanceId });
|
|
184
|
+
StepsEngine.getInstance().emit('stepEnqueued', { workflowName: stepsName, step: nextStep, state: newState, instanceId });
|
|
177
185
|
} catch (error) {
|
|
178
186
|
console.log('error', error.message);
|
|
179
187
|
throw error;
|
|
180
188
|
}
|
|
181
189
|
});
|
|
182
190
|
} catch (error) {
|
|
183
|
-
console.error('Error in step function:', error.message);
|
|
191
|
+
console.error('Error in step function: '+nextStep, error.message);
|
|
184
192
|
const connection = await Datastore.open();
|
|
185
193
|
await connection.updateOne(StepsEngine.collectionName,
|
|
186
194
|
{ _id: instanceId },
|
|
@@ -203,18 +211,18 @@ class StepsEngine extends EventEmitter {
|
|
|
203
211
|
* @throws {Error} If step definition is invalid
|
|
204
212
|
*/
|
|
205
213
|
async register(app, name, description, definition) {
|
|
206
|
-
StepsEngine.getInstance().emit('
|
|
214
|
+
StepsEngine.getInstance().emit('workflowCreated', { name, description });
|
|
207
215
|
|
|
208
216
|
// Validate each step in the definition
|
|
209
217
|
for (const [stepName, step] of Object.entries(definition)) {
|
|
210
218
|
try {
|
|
211
219
|
if (stepName !== null) {
|
|
212
|
-
app.worker(`${StepsEngine.queuePrefix}`, async (req, res) => {
|
|
220
|
+
app.worker(`${StepsEngine.queuePrefix}_${name}`, async (req, res) => {
|
|
213
221
|
const { stepsName, goto, state, instanceId, options } = req.body.payload;
|
|
214
222
|
try {
|
|
215
223
|
const qid = await StepsEngine.getInstance().handleNextStep(stepsName, goto, state, instanceId, options);
|
|
216
224
|
} catch (error) {
|
|
217
|
-
console.error('Error in step function:', error.message);
|
|
225
|
+
console.error('Error in step function: ' + stepName, error.message);
|
|
218
226
|
} finally {
|
|
219
227
|
res.end();
|
|
220
228
|
}
|
|
@@ -238,7 +246,7 @@ class StepsEngine extends EventEmitter {
|
|
|
238
246
|
* @throws {Error} If starting steps fails
|
|
239
247
|
*/
|
|
240
248
|
async start(name, initialState) {
|
|
241
|
-
StepsEngine.getInstance().emit('
|
|
249
|
+
StepsEngine.getInstance().emit('workflowStarted', { name, initialState });
|
|
242
250
|
|
|
243
251
|
return new Promise(async (resolve, reject) => {
|
|
244
252
|
try {
|
|
@@ -253,7 +261,7 @@ class StepsEngine extends EventEmitter {
|
|
|
253
261
|
// Create a new steps state in the database
|
|
254
262
|
const newState = await connection.insertOne(StepsEngine.collectionName,
|
|
255
263
|
{ ...initialState, nextStep: firstStepName, createdAt: new Date().toISOString() });
|
|
256
|
-
const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}`, {
|
|
264
|
+
const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}_${name}`, {
|
|
257
265
|
stepsName: name,
|
|
258
266
|
goto: firstStepName,
|
|
259
267
|
state: newState,
|
|
@@ -316,7 +324,7 @@ class StepsEngine extends EventEmitter {
|
|
|
316
324
|
}
|
|
317
325
|
|
|
318
326
|
console.log('continue state', state);
|
|
319
|
-
StepsEngine.getInstance().emit('
|
|
327
|
+
StepsEngine.getInstance().emit('workflowContinued', { stepsName, step: state.nextStep, instanceId });
|
|
320
328
|
|
|
321
329
|
return new Promise(async (resolve, reject) => {
|
|
322
330
|
const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}`, {
|
|
@@ -364,7 +372,7 @@ class StepsEngine extends EventEmitter {
|
|
|
364
372
|
* @returns {Promise<Object>} The cancellation result
|
|
365
373
|
*/
|
|
366
374
|
async cancelSteps(id) {
|
|
367
|
-
StepsEngine.getInstance().emit('
|
|
375
|
+
StepsEngine.getInstance().emit('cancelled', { id });
|
|
368
376
|
return new Promise(async (resolve, reject) => {
|
|
369
377
|
const connection = await Datastore.open();
|
|
370
378
|
const state = await connection.updateOne(StepsEngine.collectionName,
|