codehooks-js 1.2.20 → 1.3.1

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.
@@ -1,3 +1,2 @@
1
- export function serveStatic(options: any, app: any, filestore: any): void;
1
+ export function serveStatic(options: any, app: any, filestore: any, hook: any, fsoptions: any): void;
2
2
  export function render(viewFile: any, data: any, settings: any, cb: any): Promise<any>;
3
- //# sourceMappingURL=webserver.d.mts.map
@@ -0,0 +1,123 @@
1
+ export const setCollectionName: any;
2
+ export const setQueuePrefix: any;
3
+ export const Steps: StepsEngine;
4
+ declare const _default: StepsEngine;
5
+ export default _default;
6
+ /**
7
+ * StepsEngine class that manages step-based workflows
8
+ * @extends EventEmitter
9
+ */
10
+ declare class StepsEngine extends EventEmitter<[never]> {
11
+ static instance: any;
12
+ static collectionName: string;
13
+ static queuePrefix: string;
14
+ /**
15
+ * Set the collection name for storing steps data
16
+ * @param {string} name - Collection name
17
+ * @throws {Error} If name is not a non-empty string
18
+ */
19
+ static setCollectionName(name: string): void;
20
+ /**
21
+ * Set the queue prefix for steps jobs
22
+ * @param {string} prefix - Queue prefix
23
+ * @throws {Error} If prefix is not a non-empty string
24
+ */
25
+ static setQueuePrefix(prefix: string): void;
26
+ /**
27
+ * Get the singleton instance of StepsEngine
28
+ * @returns {StepsEngine} The singleton instance
29
+ */
30
+ static getInstance(): StepsEngine;
31
+ constructor();
32
+ definitions: Map<any, any>;
33
+ /**
34
+ * Get the step definition for a specific step
35
+ * @param {string} stepsName - Name of the steps workflow
36
+ * @param {string} stepName - Name of the step
37
+ * @returns {Function} The step function
38
+ * @throws {Error} If steps definition or step function not found
39
+ */
40
+ getDefinition(stepsName: string, stepName: string): Function;
41
+ /**
42
+ * Validate a step definition
43
+ * @param {Function} step - The step function to validate
44
+ * @throws {Error} If step definition is invalid
45
+ */
46
+ validateStepDefinition(step: Function): void;
47
+ /**
48
+ * Handle the next step in the workflow
49
+ * @param {string} stepsName - Name of the steps workflow
50
+ * @param {string|null} nextStep - Name of the next step or null if finished
51
+ * @param {Object} newState - The new state to set
52
+ * @param {string} instanceId - ID of the steps instance
53
+ * @param {Object} options - Step options (waitForInput, timeout, retry, abort)
54
+ * @returns {Promise<string|string[]>} Queue ID(s) for the next step(s)
55
+ * @throws {Error} If step execution fails
56
+ */
57
+ handleNextStep(stepsName: string, nextStep: string | null, newState: any, instanceId: string, options: any): Promise<string | string[]>;
58
+ /**
59
+ * Register a new steps workflow
60
+ * @param {Codehooks} app - Codehooks application instance
61
+ * @param {string} name - Unique identifier for the steps workflow
62
+ * @param {string} description - Human-readable description
63
+ * @param {Object} definition - Object containing step definitions
64
+ * @returns {Promise<string>} The registered steps name
65
+ * @throws {Error} If step definition is invalid
66
+ */
67
+ register(app: Codehooks, name: string, description: string, definition: any): Promise<string>;
68
+ /**
69
+ * Start a new steps instance
70
+ * @param {string} name - Name of the steps workflow to start
71
+ * @param {Object} initialState - Initial state for the steps instance
72
+ * @returns {Promise<{id: string}>} The steps instance ID
73
+ * @throws {Error} If starting steps fails
74
+ */
75
+ start(name: string, initialState: any): Promise<{
76
+ id: string;
77
+ }>;
78
+ /**
79
+ * Update the state of a steps instance
80
+ * @param {string} stepsName - Name of the steps workflow
81
+ * @param {string} instanceId - ID of the steps instance
82
+ * @param {Object} state - New state to update with
83
+ * @param {Object} options - Options for the update, { continue: false } to avoid continuing the the step
84
+ * @returns {Promise<Object>} The updated state
85
+ */
86
+ updateState(stepsName: string, instanceId: string, state: any, options?: any): Promise<any>;
87
+ /**
88
+ * Set the complete state of a steps instance
89
+ * @param {string} instanceId - ID of the steps instance
90
+ * @param {Object} state - Complete state to set
91
+ * @returns {Promise<void>}
92
+ */
93
+ setState(instanceId: string, { _id, state }: any): Promise<void>;
94
+ /**
95
+ * Continue a paused steps instance
96
+ * @param {string} stepsName - Name of the steps workflow
97
+ * @param {string} instanceId - ID of the steps instance
98
+ * @returns {Promise<{qId: string}>} Queue ID for the continued step
99
+ * @throws {Error} If steps instance not found
100
+ */
101
+ continue(stepsName: string, instanceId: string): Promise<{
102
+ qId: string;
103
+ }>;
104
+ /**
105
+ * Get the status of a steps instance
106
+ * @param {string} id - ID of the steps instance
107
+ * @returns {Promise<Object>} The steps status
108
+ */
109
+ getStepsStatus(id: string): Promise<any>;
110
+ /**
111
+ * List all steps instances matching a filter
112
+ * @param {Object} filter - Filter criteria for steps workflows
113
+ * @returns {Promise<Array>} List of steps instances
114
+ */
115
+ getInstances(filter: any): Promise<any[]>;
116
+ /**
117
+ * Cancel a steps instance
118
+ * @param {string} id - ID of the steps instance to cancel
119
+ * @returns {Promise<Object>} The cancellation result
120
+ */
121
+ cancelSteps(id: string): Promise<any>;
122
+ }
123
+ import { EventEmitter } from 'events';
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.mts","sourceRoot":"","sources":["../../workflow/engine.mjs"],"names":[],"mappings":"AAsXA,oCAAiF;AACjF,iCAA2E;AAG3E,gCAA+C;;;AAnX/C;;;GAGG;AACH;IAQI,qBAAuB;IAGvB,8BAAoC;IACpC,2BAAkC;IAElC;;;;OAIG;IACH,+BAHW,MAAM,QAQhB;IAED;;;;OAIG;IACH,8BAHW,MAAM,QAQhB;IAED;;;OAGG;IACH,sBAFa,WAAW,CAOvB;IA3CG,iBAA4B;IA6ChC;;;;;;OAMG;IACH,yBALW,MAAM,YACN,MAAM,YAehB;IAED;;;;OAIG;IACH,6CAOC;IAED;;;;;;;;;OASG;IACH,0BARW,MAAM,YACN,MAAM,GAAC,IAAI,6BAEX,MAAM,iBAEJ,OAAO,CAAC,MAAM,GAAC,MAAM,EAAE,CAAC,CA4FpC;IAED;;;;;;;;OAQG;IACH,cAPW,SAAS,QACT,MAAM,eACN,MAAM,oBAEJ,OAAO,CAAC,MAAM,CAAC,CA6B3B;IAED;;;;;;OAMG;IACH,YALW,MAAM,sBAEJ,OAAO,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAC,CAAC,CAgCjC;IAED;;;;;;OAMG;IACH,uBALW,MAAM,cACN,MAAM,eAEJ,OAAO,KAAQ,CAW3B;IAED;;;;;OAKG;IACH,qBAJW,MAAM,wBAEJ,OAAO,CAAC,IAAI,CAAC,CAKzB;IAED;;;;;;OAMG;IACH,oBALW,MAAM,cACN,MAAM,GACJ,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,CAAC,CAwBlC;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACJ,OAAO,KAAQ,CAQ3B;IAED;;;;OAIG;IACH,wBAFa,OAAO,OAAO,CAS1B;IAED;;;;OAIG;IACH,gBAHW,MAAM,GACJ,OAAO,KAAQ,CAW3B;CACJ"}
@@ -0,0 +1,10 @@
1
+ export namespace StepsConfig {
2
+ export { setCollectionName };
3
+ export { setQueuePrefix };
4
+ }
5
+ export { Steps };
6
+ export default StepsEngine;
7
+ import { setCollectionName } from './engine.mjs';
8
+ import { setQueuePrefix } from './engine.mjs';
9
+ import { Steps } from './engine.mjs';
10
+ import StepsEngine from './engine.mjs';
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../workflow/index.mjs"],"names":[],"mappings":";;;;;;kCAAsE,cAAc;+BAAd,cAAc;sBAAd,cAAc;wBAAd,cAAc"}
package/webserver.mjs CHANGED
@@ -69,8 +69,9 @@ export function serveStatic(options, app, filestore, hook, fsoptions) {
69
69
 
70
70
  // get mime type
71
71
  const type = mime.getType(filePath)
72
- console.debug('Serve file', filePath, type)
72
+
73
73
  const filestream = await filestore.getReadStream(filePath, fsoptions);
74
+ console.debug('Serve file', filePath, type)
74
75
  res.set('Content-Type', type || 'application/octet-stream');
75
76
  if (options.headers) {
76
77
  res.headers(options.headers);
@@ -91,7 +92,7 @@ export function serveStatic(options, app, filestore, hook, fsoptions) {
91
92
  })
92
93
 
93
94
  } catch (error) {
94
- console.debug("Error message", error.message)
95
+ console.debug("Error serving file", filePath, error.message)
95
96
  if (options.notFound) {
96
97
  // read default file
97
98
  // add directory filesystem path
@@ -103,6 +104,7 @@ export function serveStatic(options, app, filestore, hook, fsoptions) {
103
104
  const type = mime.getType(filePath)
104
105
  const filestream = await filestore.getReadStream(filePath, fsoptions);
105
106
  res.set('Content-Type', type || 'application/octet-stream');
107
+ res.status(404);
106
108
  filestream
107
109
  .on('data', (buf) => {
108
110
  res.write(buf, 'buffer')
@@ -168,28 +170,53 @@ async function ejs(viewFile, data, settings, cb) {
168
170
  }
169
171
  }
170
172
 
173
+ let layoutFile = null;
171
174
  async function handlebars(viewFile, data, settings, cb) {
172
175
  try {
176
+ const db = await Datastore.open();
173
177
  let engine = settings['view engine'].hbs || settings['view engine'].handlebars;
174
178
  let layout = settings['layout'];
175
179
  let views = settings['views'];
176
180
  let tmpl = null;
177
- if (tmplCache[viewFile]) {
178
- tmpl = tmplCache[viewFile];
179
- } else {
180
- //console.log('Hbs view init', layout, views)
181
- if (layout) {
182
- const layoutPath = `${layout}`;
183
- //console.log('Layout path', layoutPath)
184
- const layoutFile = await _coho_fs.readFile(layoutPath, {source: true});
185
- //console.log('Layout file', layoutFile)
186
- engine.registerPartial('layout', layoutFile);
181
+ //console.log('Hbs view init', layout, views)
182
+ if (layout) {
183
+ const layoutPath = `${layout}`;
184
+
185
+ if (!layoutFile) {
186
+
187
+ const cachedLayout = await db.get(`hbs:layout:${layout}`);
188
+ if (cachedLayout) {
189
+ console.debug('Layout load from cache', layoutPath)
190
+ layoutFile = cachedLayout;
191
+ } else {
192
+ console.debug('Layout load from file', layoutPath)
193
+ layoutFile = await _coho_fs.readFile(layoutPath, {source: true});
194
+ db.set(`hbs:layout:${layout}`, layoutFile);
195
+ }
187
196
  }
188
- const txtfile = await _coho_fs.readFile(`${settings.views}/${viewFile}.hbs`, {source: true});
189
- //console.log('Txtfile', txtfile)
197
+ //console.log('Layout file', layoutFile)
198
+ engine.registerPartial('layout', layoutFile);
199
+ }
200
+ let txtfile = await db.get(`hbs:${viewFile}`);
201
+
202
+ if (tmplCache[viewFile] !== undefined) {
203
+ console.debug('Render view from cache', tmplCache[viewFile])
204
+ tmpl = tmplCache[viewFile];
205
+ return cb(null, tmpl(data));
206
+ }
207
+ if (txtfile) {
208
+ console.debug('Render view from db')
190
209
  tmplCache[viewFile] = engine.compile(txtfile);
191
210
  tmpl = tmplCache[viewFile];
192
- }
211
+ return cb(null, tmpl(data));
212
+ }
213
+
214
+
215
+ txtfile = await _coho_fs.readFile(`${settings.views}/${viewFile}.hbs`, {source: true});
216
+ console.debug('Creating hbs template', viewFile)
217
+ tmplCache[viewFile] = engine.compile(txtfile);
218
+ tmpl = tmplCache[viewFile];
219
+ db.set(`hbs:${viewFile}`, txtfile);
193
220
  cb(null, tmpl(data));
194
221
  } catch (err) {
195
222
  console.error('Handle bars engine error:', err)
@@ -0,0 +1,384 @@
1
+ /*
2
+ Implements a steps engine that uses Codehooks.io as the state storage and queues for persistent workers.
3
+ The engine manages step transitions, state persistence, and event handling for step-based workflows.
4
+ */
5
+
6
+ import { EventEmitter } from 'events';
7
+
8
+ /**
9
+ * StepsEngine class that manages step-based workflows
10
+ * @extends EventEmitter
11
+ */
12
+ class StepsEngine extends EventEmitter {
13
+
14
+ constructor() {
15
+ super();
16
+ this.definitions = new Map();
17
+ }
18
+
19
+ // Singleton instance
20
+ static instance = null;
21
+
22
+ // Configuration
23
+ static collectionName = 'stepsdata';
24
+ static queuePrefix = 'stepsqueue';
25
+
26
+ /**
27
+ * Set the collection name for storing steps data
28
+ * @param {string} name - Collection name
29
+ * @throws {Error} If name is not a non-empty string
30
+ */
31
+ static setCollectionName(name) {
32
+ if (typeof name !== 'string' || !name.trim()) {
33
+ throw new Error('Collection name must be a non-empty string');
34
+ }
35
+ StepsEngine.collectionName = name.trim();
36
+ }
37
+
38
+ /**
39
+ * Set the queue prefix for steps jobs
40
+ * @param {string} prefix - Queue prefix
41
+ * @throws {Error} If prefix is not a non-empty string
42
+ */
43
+ static setQueuePrefix(prefix) {
44
+ if (typeof prefix !== 'string' || !prefix.trim()) {
45
+ throw new Error('Queue prefix must be a non-empty string');
46
+ }
47
+ StepsEngine.queuePrefix = prefix.trim();
48
+ }
49
+
50
+ /**
51
+ * Get the singleton instance of StepsEngine
52
+ * @returns {StepsEngine} The singleton instance
53
+ */
54
+ static getInstance() {
55
+ if (!StepsEngine.instance) {
56
+ StepsEngine.instance = new StepsEngine();
57
+ }
58
+ return StepsEngine.instance;
59
+ }
60
+
61
+ /**
62
+ * Get the step definition for a specific step
63
+ * @param {string} stepsName - Name of the steps workflow
64
+ * @param {string} stepName - Name of the step
65
+ * @returns {Function} The step function
66
+ * @throws {Error} If steps definition or step function not found
67
+ */
68
+ getDefinition(stepsName, stepName) {
69
+ const stepsDef = this.definitions.get(stepsName);
70
+ if (!stepsDef) {
71
+ throw new Error(`No Steps definition found for: ${stepsName}`);
72
+ }
73
+ // Get the specific step function from that workflow
74
+ const stepFunc = stepsDef[stepName];
75
+ if (!stepFunc) {
76
+ throw new Error(`No step function found for: ${stepName} in steps ${stepsName}`);
77
+ }
78
+ return stepFunc;
79
+ }
80
+
81
+ /**
82
+ * Validate a step definition
83
+ * @param {Function} step - The step function to validate
84
+ * @throws {Error} If step definition is invalid
85
+ */
86
+ validateStepDefinition(step) {
87
+ if (typeof step !== 'function') {
88
+ throw new Error('Step definition must be a function');
89
+ }
90
+ if (step.length !== 2) {
91
+ throw new Error('Step definition must accept exactly 2 parameters');
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Handle the next step in the workflow
97
+ * @param {string} stepsName - Name of the steps workflow
98
+ * @param {string|null} nextStep - Name of the next step or null if finished
99
+ * @param {Object} newState - The new state to set
100
+ * @param {string} instanceId - ID of the steps instance
101
+ * @param {Object} options - Step options (waitForInput, timeout, retry, abort)
102
+ * @returns {Promise<string|string[]>} Queue ID(s) for the next step(s)
103
+ * @throws {Error} If step execution fails
104
+ */
105
+ async handleNextStep(stepsName, nextStep, newState, instanceId, options) {
106
+ try {
107
+ // Handle array of next steps
108
+ if (Array.isArray(nextStep)) {
109
+ const results = [];
110
+ for (const step of nextStep) {
111
+ const qid = await this.handleNextStep(stepsName, step, newState, instanceId, options);
112
+ results.push(qid);
113
+ }
114
+ return results;
115
+ }
116
+
117
+ const isFinished = nextStep === null;
118
+
119
+ // Handle single next step
120
+ StepsEngine.getInstance().emit('stepStarted', { stepsName, step: nextStep, state: newState, instanceId });
121
+ const connection = await Datastore.open();
122
+ // remove the _id from the newState
123
+ delete newState._id;
124
+ // Update the existing steps state in the database
125
+ newState = await connection.updateOne(StepsEngine.collectionName,
126
+ { _id: instanceId },
127
+ { $set: { ...newState, nextStep: nextStep, updatedAt: new Date().toISOString(), isFinished: isFinished } });
128
+
129
+ StepsEngine.getInstance().emit('stateUpdated', { stepsName, state: newState, instanceId });
130
+
131
+ // Get the next step function
132
+ const func = StepsEngine.getInstance().getDefinition(stepsName, nextStep);
133
+
134
+ // Check if the step is waiting for input
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 });
138
+ return;
139
+ }
140
+
141
+ try {
142
+ // Call the next step function with the step context
143
+ func.call(this, {...newState, instanceId: newState._id}, async function (nextStep, userState, options) {
144
+ try {
145
+ if (nextStep === null) {
146
+ console.log('No next step, steps workflow completed');
147
+ StepsEngine.getInstance().emit('completed', { message: 'Steps workflow completed', ...userState });
148
+ return;
149
+ }
150
+ // Protect system-level properties
151
+ const protectedState = {
152
+ _id: newState._id,
153
+ nextStep: newState.nextStep,
154
+ createdAt: newState.createdAt,
155
+ updatedAt: newState.updatedAt,
156
+ instanceId: newState.instanceId,
157
+ isFinished: newState.isFinished
158
+ };
159
+
160
+ // Merge states with userState taking precedence, but protecting system fields
161
+ const mergedState = {
162
+ ...newState,
163
+ ...userState,
164
+ ...protectedState
165
+ };
166
+
167
+ // Enqueue the next step
168
+ const qres = connection.enqueue(`${StepsEngine.queuePrefix}`, {
169
+ stepsName: stepsName,
170
+ goto: nextStep,
171
+ state: mergedState,
172
+ options: options,
173
+ instanceId: instanceId
174
+ });
175
+
176
+ StepsEngine.getInstance().emit('stepEnqueued', { stepsName, step: nextStep, state: newState, instanceId });
177
+ } catch (error) {
178
+ console.log('error', error.message);
179
+ throw error;
180
+ }
181
+ });
182
+ } catch (error) {
183
+ console.error('Error in step function:', error.message);
184
+ const connection = await Datastore.open();
185
+ await connection.updateOne(StepsEngine.collectionName,
186
+ { _id: instanceId },
187
+ { $set: { lastError: error.message, updatedAt: new Date().toISOString() } });
188
+ throw error;
189
+ }
190
+ }
191
+ catch (error) {
192
+ throw error;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Register a new steps workflow
198
+ * @param {Codehooks} app - Codehooks application instance
199
+ * @param {string} name - Unique identifier for the steps workflow
200
+ * @param {string} description - Human-readable description
201
+ * @param {Object} definition - Object containing step definitions
202
+ * @returns {Promise<string>} The registered steps name
203
+ * @throws {Error} If step definition is invalid
204
+ */
205
+ async register(app, name, description, definition) {
206
+ StepsEngine.getInstance().emit('stepsRegistered', { name, description });
207
+
208
+ // Validate each step in the definition
209
+ for (const [stepName, step] of Object.entries(definition)) {
210
+ try {
211
+ if (stepName !== null) {
212
+ app.worker(`${StepsEngine.queuePrefix}`, async (req, res) => {
213
+ const { stepsName, goto, state, instanceId, options } = req.body.payload;
214
+ try {
215
+ const qid = await StepsEngine.getInstance().handleNextStep(stepsName, goto, state, instanceId, options);
216
+ } catch (error) {
217
+ console.error('Error in step function:', error.message);
218
+ } finally {
219
+ res.end();
220
+ }
221
+ });
222
+ }
223
+ } catch (error) {
224
+ console.error(`Invalid step definition '${stepName}': ${error.message}`);
225
+ throw error;
226
+ }
227
+ }
228
+
229
+ this.definitions.set(name, definition);
230
+ return name;
231
+ }
232
+
233
+ /**
234
+ * Start a new steps instance
235
+ * @param {string} name - Name of the steps workflow to start
236
+ * @param {Object} initialState - Initial state for the steps instance
237
+ * @returns {Promise<{id: string}>} The steps instance ID
238
+ * @throws {Error} If starting steps fails
239
+ */
240
+ async start(name, initialState) {
241
+ StepsEngine.getInstance().emit('stepsStarted', { name, initialState });
242
+
243
+ return new Promise(async (resolve, reject) => {
244
+ try {
245
+ const funcs = this.definitions.get(name);
246
+ const firstStepName = Object.keys(funcs)[0];
247
+ const firstStep = funcs[firstStepName];
248
+ if (!firstStep) {
249
+ reject(new Error('No start step defined in steps'));
250
+ return;
251
+ }
252
+ const connection = await Datastore.open();
253
+ // Create a new steps state in the database
254
+ const newState = await connection.insertOne(StepsEngine.collectionName,
255
+ { ...initialState, nextStep: firstStepName, createdAt: new Date().toISOString() });
256
+ const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}`, {
257
+ stepsName: name,
258
+ goto: firstStepName,
259
+ state: newState,
260
+ instanceId: newState._id,
261
+ options: {}
262
+ });
263
+ resolve(newState);
264
+ } catch (error) {
265
+ console.error('Error starting steps:', error.message);
266
+ reject(error);
267
+ }
268
+ });
269
+ }
270
+
271
+ /**
272
+ * Update the state of a steps instance
273
+ * @param {string} stepsName - Name of the steps workflow
274
+ * @param {string} instanceId - ID of the steps instance
275
+ * @param {Object} state - New state to update with
276
+ * @param {Object} options - Options for the update, { continue: false } to avoid continuing the the step
277
+ * @returns {Promise<Object>} The updated state
278
+ */
279
+ async updateState(stepsName, instanceId, state, options={continue: true}) {
280
+ StepsEngine.getInstance().emit('stepsStateUpdating', { stepsName, instanceId, state });
281
+ const connection = await Datastore.open();
282
+ return new Promise(async (resolve, reject) => {
283
+ const doc = await connection.updateOne(StepsEngine.collectionName,
284
+ { _id: instanceId },
285
+ { $set: { ...state, updatedAt: new Date().toISOString() } });
286
+ if (options.continue) {
287
+ await this.continue(stepsName, instanceId);
288
+ }
289
+ resolve({ ...doc });
290
+ });
291
+ }
292
+
293
+ /**
294
+ * Set the complete state of a steps instance
295
+ * @param {string} instanceId - ID of the steps instance
296
+ * @param {Object} state - Complete state to set
297
+ * @returns {Promise<void>}
298
+ */
299
+ async setState(instanceId, { _id, state }) {
300
+ const connection = await Datastore.open();
301
+ await connection.replaceOne(StepsEngine.collectionName, { _id: _id }, { ...state });
302
+ }
303
+
304
+ /**
305
+ * Continue a paused steps instance
306
+ * @param {string} stepsName - Name of the steps workflow
307
+ * @param {string} instanceId - ID of the steps instance
308
+ * @returns {Promise<{qId: string}>} Queue ID for the continued step
309
+ * @throws {Error} If steps instance not found
310
+ */
311
+ async continue(stepsName, instanceId) {
312
+ const connection = await Datastore.open();
313
+ const state = await connection.findOne(StepsEngine.collectionName, { _id: instanceId });
314
+ if (!state) {
315
+ throw new Error(`No steps found with instanceId: ${instanceId}`);
316
+ }
317
+
318
+ console.log('continue state', state);
319
+ StepsEngine.getInstance().emit('stepContinued', { stepsName, step: state.nextStep, instanceId });
320
+
321
+ return new Promise(async (resolve, reject) => {
322
+ const { _id: ID } = await connection.enqueue(`${StepsEngine.queuePrefix}`, {
323
+ stepsName,
324
+ goto: state.nextStep,
325
+ state: state,
326
+ options: {},
327
+ instanceId: instanceId
328
+ });
329
+
330
+ resolve({ qId: ID });
331
+ });
332
+ }
333
+
334
+ /**
335
+ * Get the status of a steps instance
336
+ * @param {string} id - ID of the steps instance
337
+ * @returns {Promise<Object>} The steps status
338
+ */
339
+ async getStepsStatus(id) {
340
+ return new Promise(async (resolve, reject) => {
341
+ const connection = await Datastore.open();
342
+ const state = await connection.findOne(StepsEngine.collectionName, { _id: id });
343
+ resolve(state);
344
+ });
345
+ }
346
+
347
+ /**
348
+ * List all steps instances matching a filter
349
+ * @param {Object} filter - Filter criteria for steps workflows
350
+ * @returns {Promise<Array>} List of steps instances
351
+ */
352
+ async getInstances(filter) {
353
+ return new Promise(async (resolve, reject) => {
354
+ const connection = await Datastore.open();
355
+ const states = await connection.find(StepsEngine.collectionName, filter).toArray();
356
+ console.log('listSteps', StepsEngine.collectionName, filter, states.length);
357
+ resolve(states);
358
+ });
359
+ }
360
+
361
+ /**
362
+ * Cancel a steps instance
363
+ * @param {string} id - ID of the steps instance to cancel
364
+ * @returns {Promise<Object>} The cancellation result
365
+ */
366
+ async cancelSteps(id) {
367
+ StepsEngine.getInstance().emit('stepsCancelled', { id });
368
+ return new Promise(async (resolve, reject) => {
369
+ const connection = await Datastore.open();
370
+ const state = await connection.updateOne(StepsEngine.collectionName,
371
+ { _id: id },
372
+ { $set: { status: 'cancelled' } });
373
+ resolve(state);
374
+ });
375
+ }
376
+ }
377
+
378
+ // Export the static methods directly
379
+ export const setCollectionName = StepsEngine.setCollectionName.bind(StepsEngine);
380
+ export const setQueuePrefix = StepsEngine.setQueuePrefix.bind(StepsEngine);
381
+
382
+ // Export the singleton instance
383
+ export const Steps = StepsEngine.getInstance();
384
+ export default StepsEngine.getInstance();
@@ -0,0 +1,13 @@
1
+ import StepsEngine, { setCollectionName, setQueuePrefix, Steps } from './engine.mjs';
2
+
3
+ // Re-export the static methods
4
+ export const StepsConfig = {
5
+ setCollectionName,
6
+ setQueuePrefix
7
+ };
8
+
9
+ // Re-export the workflow instance
10
+ export { Steps };
11
+
12
+ // Export the engine as default
13
+ export default StepsEngine;