codehooks-js 1.3.10 → 1.3.12

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/README.md CHANGED
@@ -1,19 +1,28 @@
1
1
  # codehooks-js
2
2
 
3
- Codehooks.io official development library - provides Express.JS-like syntax for using codehooks.io.
3
+ The official JavaScript/TypeScript library for [Codehooks.io](https://codehooks.io) - a serverless backend platform that provides everything you need to build and deploy backend applications.
4
4
 
5
- Read the [Quickstart for developers](https://codehooks.io/docs/quickstart-cli).
5
+ ## What is Codehooks.io?
6
6
 
7
- For more details navigate to official docs: [https://codehooks.io/docs](https://codehooks.io/docs).
7
+ Codehooks.io is a complete serverless backend platform that combines:
8
+ - **Database** - NoSQL document storage with MongoDB-like queries
9
+ - **API Server** - Express.js-style routing and middleware
10
+ - **Background Jobs** - Queues, workers, and scheduled tasks
11
+ - **Real-time** - Server-sent events for live updates
12
+ - **File Storage** - Static file serving and blob storage
13
+ - **Workflows** - Step-based application logic
8
14
 
9
- ## Install
15
+ All in one platform with zero infrastructure management.
16
+
17
+ ## Quick Start
10
18
 
11
19
  Install the [Codehooks CLI](https://codehooks.io/docs/cli):
12
20
 
13
21
  ```shell
14
22
  $ npm install -g codehooks
15
23
  ```
16
- Next install the codehooks-js NPM package in your project.
24
+
25
+ Install the codehooks-js library in your project:
17
26
 
18
27
  ```shell
19
28
  npm install codehooks-js
@@ -114,12 +123,200 @@ Correcting the errors should ultimately show a successfull compile output, ready
114
123
  $ coho compile
115
124
 
116
125
  OK 🙌
117
-
118
126
  ```
119
127
 
128
+ Your backend application is now available at your project's endpoint URL. For example:
129
+
130
+ `https://myproject-ff00.codehooks.io/dev/*`
131
+
120
132
  ## Deploy
121
133
 
122
134
  From the project directory run:
123
135
  ```shell
124
136
  $ codehooks deploy
125
- ```
137
+ ```
138
+
139
+ ## Documentation
140
+
141
+ For complete documentation, visit [https://codehooks.io/docs](https://codehooks.io/docs).
142
+
143
+ ## API
144
+
145
+ ## Codehooks Class API Reference
146
+
147
+ The Codehooks class provides a comprehensive backend application framework with the following APIs:
148
+
149
+ ### **HTTP Routing APIs**
150
+ - **`post(path, ...hook)`** - Register POST route handlers
151
+ - **`get(path, ...hook)`** - Register GET route handlers
152
+ - **`put(path, ...hook)`** - Register PUT route handlers
153
+ - **`patch(path, ...hook)`** - Register PATCH route handlers
154
+ - **`delete(path, ...hook)`** - Register DELETE route handlers
155
+ - **`all(path, ...hook)`** - Register handlers for all HTTP methods
156
+
157
+ ### **Middleware & Authentication APIs**
158
+ - **`use(...hook)`** - Register global middleware (supports string paths, RegExp, or function)
159
+ - **`useRoute(route, ...hook)`** - Register route-specific middleware
160
+ - **`auth(path, ...hook)`** - Register authentication middleware for specific paths
161
+
162
+ ### **Background Processing APIs**
163
+ - **`queue(topic, ...hook)`** - Register queue handlers for background processing
164
+ - **`worker(name, ...hook)`** - Register worker functions (also adds to queues for legacy support)
165
+ - **`job(cronExpression, ...hook)`** - Register scheduled cron jobs
166
+
167
+ ### **Static File Serving APIs**
168
+ - **`static(options, hook)`** - Serve static files from source code directory
169
+ - **`storage(options, hook)`** - Serve files from blob storage directory
170
+
171
+ ### **Template & Configuration APIs**
172
+ - **`set(key, val)`** - Set application configuration settings
173
+ - **`render(view, data, cb)`** - Render templates with data
174
+ - **`crudlify(schema, options)`** - Auto-generate CRUD REST API endpoints
175
+
176
+ ### **Real-time Communication APIs**
177
+ - **`realtime(path, ...hook)`** - Set up server-sent events (SSE) channels for real-time communication
178
+
179
+ ### **Workflow Management APIs**
180
+ - **`createWorkflow(name, description, steps, options)`** - Create and register a new workflow instance
181
+
182
+ ### **Application Lifecycle APIs**
183
+ - **`init(hook)`** - Initialize the application and return manifest
184
+ - **`start(hook)`** - Alias for `init()` method
185
+
186
+
187
+
188
+ The Codehooks class serves as a comprehensive backend application framework that combines HTTP routing, background processing, real-time communication, and workflow management capabilities in a single, cohesive API.
189
+
190
+ ## Datastore API Reference
191
+
192
+ The Datastore provides a unified interface for both NoSQL document storage and Key-Value operations. It supports MongoDB-like query syntax and provides streaming capabilities for large datasets.
193
+
194
+ ### **Connection & Collection Management**
195
+ - **`open()`** - Connect to the Datastore and return the API interface
196
+ - **`collection(name)`** - Get a NoSQL collection by name for document operations
197
+
198
+ ### **NoSQL Document Operations**
199
+
200
+ #### **Read Operations**
201
+ - **`getOne(collection, query)`** - Get a single document by ID or query
202
+ - **`findOne(collection, query)`** - Alias for getOne
203
+ - **`getMany(collection, query?, options?)`** - Get a stream of documents matching query
204
+ - **`find(collection, query?, options?)`** - Alias for getMany
205
+
206
+ #### **Write Operations**
207
+ - **`insertOne(collection, document)`** - Insert a new document into a collection
208
+ - **`updateOne(collection, query, document, updateOperators?, options?)`** - Update one document (patches existing data)
209
+ - **`updateMany(collection, query, document, updateOperators?)`** - Update multiple documents
210
+ - **`replaceOne(collection, query, document, options?)`** - Replace one document completely
211
+ - **`replaceMany(collection, query, document, options?)`** - Replace multiple documents
212
+
213
+ #### **Delete Operations**
214
+ - **`removeOne(collection, query)`** - Remove one document by ID or query
215
+ - **`removeMany(collection, query)`** - Remove multiple documents matching query
216
+
217
+ #### **Schema Management**
218
+ - **`createSchema(collection, schema)`** - Validate collection data against JSON-Schema
219
+ - **`setSchema(collection, schema)`** - Alias for createSchema
220
+ - **`removeSchema(collection)`** - Remove JSON-schema validation for collection
221
+ - **`getSchema(collection)`** - Get JSON-schema for collection
222
+
223
+ #### **Utility Operations**
224
+ - **`count(collection)`** - Count documents in a collection
225
+
226
+ ### **Key-Value Operations**
227
+
228
+ #### **Basic Key-Value Operations**
229
+ - **`set(key, value, options?)`** - Set a key-value pair
230
+ - **`get(key, options?)`** - Get a value by key
231
+ - **`getAll(keyPattern, options?)`** - Get all key-value pairs matching pattern
232
+ - **`del(key, value)`** - Delete a key-value pair
233
+ - **`delAll(keyPattern, options?)`** - Delete all key-value pairs matching pattern
234
+
235
+ #### **Numeric Operations**
236
+ - **`incr(key, value, options?)`** - Increment a numeric value
237
+ - **`decr(key, value, options?)`** - Decrement a numeric value
238
+
239
+ ### **Queue Operations**
240
+ - **`enqueue(topic, document, options?)`** - Add a job to a queue for background processing
241
+ - **`enqueueFromQuery(collection, query, topic, options?)`** - Queue each item from a database query
242
+
243
+ ### **DataStream Interface**
244
+
245
+ When using `getMany()` or `find()`, you get a DataStream object with these methods:
246
+
247
+ - **`on(event, callback)`** - Listen for data events (e.g., `'data'`, `'end'`)
248
+ - **`json(response)`** - Pipe data directly to HTTP response as JSON
249
+ - **`toArray()`** - Convert stream to array of objects
250
+ - **`forEach(callback)`** - Iterate over each object in the stream
251
+
252
+ ### **Usage Examples**
253
+
254
+ ```javascript
255
+ import { datastore } from 'codehooks-js';
256
+
257
+ // Connect to datastore
258
+ const conn = await datastore.open();
259
+
260
+ // NoSQL operations
261
+ const doc = await conn.insertOne('users', { name: 'John', email: 'john@example.com' });
262
+ const user = await conn.getOne('users', { _id: doc._id });
263
+ const users = await conn.getMany('users', { active: true }).toArray();
264
+ await conn.updateOne('users', { _id: doc._id }, { lastLogin: new Date() });
265
+
266
+ // Key-Value operations
267
+ await conn.set('user:123:session', { token: 'abc123', expires: new Date() });
268
+ const session = await conn.get('user:123:session');
269
+ await conn.incr('visits', 1);
270
+
271
+ // Queue operations
272
+ await conn.enqueue('emailWorker', { to: 'user@example.com', template: 'welcome' });
273
+ ```
274
+
275
+ ### **Query Syntax**
276
+
277
+ The Datastore supports MongoDB-like query syntax:
278
+
279
+ ```javascript
280
+ // Simple equality
281
+ { status: 'active' }
282
+
283
+ // Comparison operators
284
+ { age: { $gt: 18 } }
285
+ { price: { $lte: 100 } }
286
+
287
+ // Logical operators
288
+ { $and: [{ status: 'active' }, { age: { $gte: 18 } }] }
289
+ { $or: [{ category: 'A' }, { category: 'B' }] }
290
+
291
+ // Array operations
292
+ { tags: { $in: ['javascript', 'nodejs'] } }
293
+ { tags: { $all: ['javascript', 'nodejs'] } }
294
+
295
+ // Regular expressions
296
+ { name: { $regex: /john/i } }
297
+ ```
298
+
299
+ ### **Options**
300
+
301
+ Many operations accept an options object for additional configuration:
302
+
303
+ ```javascript
304
+ // Upsert option for updates
305
+ await conn.updateOne('users', { email: 'john@example.com' },
306
+ { lastLogin: new Date() }, {}, { upsert: true });
307
+
308
+ // Key-Value options
309
+ await conn.set('key', 'value', { ttl: 3600000 }); // 1 hour TTL
310
+ ```
311
+
312
+ ### **Key Features:**
313
+ 1. **Express-style routing** with support for all HTTP methods
314
+ 2. **Middleware system** with global and route-specific middleware
315
+ 3. **Background processing** with queues, workers, and scheduled jobs
316
+ 4. **Static file serving** from both source and blob storage
317
+ 5. **Real-time communication** via Server-Sent Events
318
+ 6. **Workflow engine** for complex step-based applications
319
+ 7. **Auto-generated CRUD APIs** with schema validation
320
+ 8. **Template rendering** system
321
+ 9. **Singleton pattern** for global application state
322
+ 10. **Datastore** with MongoDB-like query syntax, key-value operations, and queue management
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codehooks-js",
3
- "version": "1.3.10",
3
+ "version": "1.3.12",
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
@@ -1080,10 +1080,10 @@ export type WorkflowEvents = {
1080
1080
  'stateUpdated': { workflowName: string; state: any; instanceId: string };
1081
1081
 
1082
1082
  /**
1083
- * Emitted when a step is waiting for input
1083
+ * Emitted when a step is enqueued for execution
1084
1084
  * @event
1085
1085
  */
1086
- 'stepWaiting': { workflowName: string; step: string; instanceId: string };
1086
+ 'stepEnqueued': { workflowName: string; step: string; state: any; instanceId: string };
1087
1087
 
1088
1088
  /**
1089
1089
  * Emitted when a workflow instance is continued after waiting
@@ -1113,7 +1113,11 @@ export type WorkflowEvents = {
1113
1113
  /**
1114
1114
  * Definition of a workflow step function
1115
1115
  */
1116
- export type WorkflowDefinition = Record<string, (state: any, callback: (nextStep: string | string[] | null, newState: any, options?: any) => void) => Promise<void>>;
1116
+ export type WorkflowDefinition = Record<string, (
1117
+ state: any,
1118
+ callback: (nextStep: string | string[] | null, newState: any, options?: any) => void,
1119
+ waiterFunction?: (waiter?: any) => void
1120
+ ) => Promise<void>>;
1117
1121
 
1118
1122
  /**
1119
1123
  * Configuration options for a workflow step
@@ -1141,20 +1145,6 @@ export type WorkflowConfig = {
1141
1145
  steps?: Record<string, StepOptions>;
1142
1146
  };
1143
1147
 
1144
- /**
1145
- * Engine for managing workflow-based applications
1146
- * @extends Workflow
1147
- */
1148
- export type WorkflowEngine = Workflow & {
1149
- /**
1150
- * Register a new workflow
1151
- * @param name - Unique identifier for the workflow
1152
- * @param description - Human-readable description
1153
- * @param steps - Step definitions
1154
- */
1155
- register: (name: string, description: string, steps: WorkflowDefinition) => Promise<string>;
1156
- };
1157
-
1158
1148
  /**
1159
1149
  * Workflow engine for managing step-based applications
1160
1150
  * @extends EventEmitter
@@ -1185,48 +1175,118 @@ export type Workflow = {
1185
1175
  configure: (options: WorkflowConfig) => void;
1186
1176
 
1187
1177
  /**
1188
- * Register a new workflow
1178
+ * Get the collection name for storing workflow data
1179
+ * @returns The collection name
1180
+ */
1181
+ getCollectionName: () => string;
1182
+
1183
+ /**
1184
+ * Set the collection name for storing workflow data
1185
+ * @param name - Collection name
1186
+ */
1187
+ setCollectionName: (name: string) => void;
1188
+
1189
+ /**
1190
+ * Get the queue prefix for workflow jobs
1191
+ * @returns The queue prefix
1192
+ */
1193
+ getQueuePrefix: () => string;
1194
+
1195
+ /**
1196
+ * Set the queue prefix for workflow jobs
1197
+ * @param prefix - Queue prefix
1198
+ */
1199
+ setQueuePrefix: (prefix: string) => void;
1200
+
1201
+ /**
1202
+ * Get the timeout for workflow steps
1203
+ * @returns The timeout in milliseconds
1204
+ */
1205
+ getTimeout: () => number;
1206
+
1207
+ /**
1208
+ * Set the timeout for workflow steps
1209
+ * @param timeout - Timeout in milliseconds
1210
+ */
1211
+ setTimeout: (timeout: number) => void;
1212
+
1213
+ /**
1214
+ * Get the maximum step count
1215
+ * @returns The maximum step count
1216
+ */
1217
+ getMaxStepCount: () => number;
1218
+
1219
+ /**
1220
+ * Set the maximum step count
1221
+ * @param maxStepCount - Maximum step count
1222
+ */
1223
+ setMaxStepCount: (maxStepCount: number) => void;
1224
+
1225
+ /**
1226
+ * Get the steps configuration
1227
+ * @returns The steps configuration
1228
+ */
1229
+ getStepsConfig: () => Record<string, StepOptions>;
1230
+
1231
+ /**
1232
+ * Set the steps configuration
1233
+ * @param steps - Steps configuration
1234
+ */
1235
+ setStepsConfig: (steps: Record<string, StepOptions>) => void;
1236
+
1237
+ /**
1238
+ * Get the step definition for a specific step
1239
+ * @param workflowName - Name of the workflow
1240
+ * @param stepName - Name of the step
1241
+ * @returns The step function
1242
+ */
1243
+ getDefinition: (workflowName: string, stepName: string) => Function;
1244
+
1245
+ /**
1246
+ * Register a new workflow with a Codehooks application
1189
1247
  * @param app - Codehooks application instance
1190
- * @param name - Unique identifier for the workflow
1191
- * @param description - Human-readable description
1192
- * @param definition - Object containing step definitions
1193
1248
  * @returns Promise with the registered workflow name
1194
1249
  */
1195
- register: (app: Codehooks, name: string, description: string, definition: Record<string, Function>) => Promise<string>;
1250
+ register: (app: Codehooks) => Promise<string>;
1196
1251
 
1197
1252
  /**
1198
1253
  * Start a new workflow instance
1199
- * @param name - Name of the workflow to start
1200
1254
  * @param initialState - Initial state for the workflow instance
1201
- * @returns Promise with the workflow instance ID
1255
+ * @returns Promise with the workflow instance
1202
1256
  */
1203
- start: (name: string, initialState: any) => Promise<{ id: string }>;
1257
+ start: (initialState: any) => Promise<any>;
1204
1258
 
1205
1259
  /**
1206
1260
  * Update the state of a workflow instance
1207
- * @param workflowName - Name of the workflow
1208
1261
  * @param instanceId - ID of the workflow instance
1209
1262
  * @param state - New state to update with
1210
1263
  * @param options - Options for the update, { continue: false } to avoid continuing the step
1211
1264
  * @returns Promise with the updated state
1212
1265
  */
1213
- updateState: (workflowName: string, instanceId: string, state: any, options?: UpdateOptions) => Promise<any>;
1266
+ updateState: (instanceId: string, state: any, options?: UpdateOptions) => Promise<any>;
1267
+
1268
+ /**
1269
+ * Set the complete state of a workflow instance
1270
+ * @param instanceId - ID of the workflow instance
1271
+ * @param stateData - Object containing _id and state
1272
+ * @returns Promise<void>
1273
+ */
1274
+ setState: (instanceId: string, stateData: { _id: string; state: any }) => Promise<void>;
1214
1275
 
1215
1276
  /**
1216
1277
  * Continue a paused workflow instance
1217
- * @param workflowName - Name of the workflow
1218
1278
  * @param instanceId - ID of the workflow instance
1219
1279
  * @param reset - Whether to reset all step counts (true) or just the current step (false)
1220
1280
  * @returns Promise with the queue ID for the continued step
1221
1281
  */
1222
- continue: (workflowName: string, instanceId: string, reset?: boolean) => Promise<{ qId: string }>;
1282
+ continue: (instanceId: string, reset?: boolean) => Promise<{ instanceId: string }>;
1223
1283
 
1224
1284
  /**
1225
1285
  * Get the status of a workflow instance
1226
1286
  * @param id - ID of the workflow instance
1227
1287
  * @returns Promise with the workflow status
1228
1288
  */
1229
- getWorkflowStatus: (id: string) => Promise<any>;
1289
+ getStepsStatus: (id: string) => Promise<any>;
1230
1290
 
1231
1291
  /**
1232
1292
  * Get all workflow instances matching a filter
@@ -1240,7 +1300,7 @@ export type Workflow = {
1240
1300
  * @param id - ID of the workflow instance to cancel
1241
1301
  * @returns Promise with the cancellation result
1242
1302
  */
1243
- cancelWorkflow: (id: string) => Promise<any>;
1303
+ cancelSteps: (id: string) => Promise<any>;
1244
1304
 
1245
1305
  /**
1246
1306
  * Register an event listener
@@ -1278,7 +1338,7 @@ export type Workflow = {
1278
1338
  * Continue all timed out workflow instances
1279
1339
  * @returns Promise with array of results containing queue IDs for continued workflows
1280
1340
  */
1281
- continueAllTimedOut: () => Promise<Array<{qId: string}>>;
1341
+ continueAllTimedOut: () => Promise<Array<{instanceId: string}>>;
1282
1342
 
1283
1343
  /**
1284
1344
  * Check if a specific step in a workflow instance has timed out
@@ -1297,6 +1357,7 @@ export type Workflow = {
1297
1357
  * // finishTime?: string, // if step has finished
1298
1358
  * // currentTime?: string, // if step is still running
1299
1359
  * // timeoutSource: 'stepConfig' | 'defaultOptions' | 'globalTimeout'
1360
+ * // reason?: string // if step not found
1300
1361
  * // }
1301
1362
  */
1302
1363
  isStepTimedOut: (workflow: any) => {
@@ -1374,11 +1435,14 @@ export type WorkflowEvent =
1374
1435
  * Data structure for workflow events
1375
1436
  */
1376
1437
  export type WorkflowEventData = {
1377
- workflowName: string;
1438
+ workflowName?: string;
1439
+ name?: string;
1440
+ description?: string;
1378
1441
  step?: string;
1379
1442
  state?: any;
1380
1443
  instanceId?: string;
1381
1444
  error?: Error;
1445
+ id?: string;
1382
1446
  [key: string]: any;
1383
1447
  };
1384
1448
 
@@ -261,6 +261,18 @@ class Workflow extends EventEmitter {
261
261
  return;
262
262
  }
263
263
  try {
264
+ const waiterFunction = async (state) => {
265
+ if (state) {
266
+ console.debug('waiter', state);
267
+ await connection.updateOne(this.#collectionName,
268
+ { _id: instanceId },
269
+ { $set: { ...state, updatedAt: new Date().toISOString() } });
270
+ resolve();
271
+ return;
272
+ } else {
273
+ resolve(); // no state update
274
+ }
275
+ }
264
276
  await func({...newState, instanceId: newState._id}, async (nextStep, userState, options) => {
265
277
  try {
266
278
  // Protect system-level properties
@@ -370,8 +382,8 @@ class Workflow extends EventEmitter {
370
382
  console.error('error', error.message);
371
383
  reject(error);
372
384
  }
373
- });
374
- resolve();
385
+ }, waiterFunction);
386
+ //resolve();
375
387
  } catch (error) {
376
388
  console.error('Error executing step function:', error);
377
389
  reject(error);
@@ -411,17 +423,7 @@ class Workflow extends EventEmitter {
411
423
  * @private
412
424
  */
413
425
  async registerWithApp(app, name, description, definition) {
414
- this.emit('workflowCreated', { name, description });
415
-
416
- // Log initial state of definitions Map
417
- console.debug('Before registration - Current definitions Map:', {
418
- size: this.#definitions.size,
419
- keys: Array.from(this.#definitions.keys()),
420
- entries: Array.from(this.#definitions.entries()).map(([key, value]) => ({
421
- key,
422
- stepNames: Object.keys(value)
423
- }))
424
- });
426
+ this.emit('workflowCreated', { name, description });
425
427
 
426
428
  // Validate each step in the definition
427
429
  for (const [stepName, step] of Object.entries(definition)) {
@@ -456,63 +458,33 @@ class Workflow extends EventEmitter {
456
458
  }
457
459
 
458
460
  // Store the definition
459
- this.#definitions.set(name, definition);
460
-
461
- // Log state after registration
462
- console.debug('After registration - Updated definitions Map:', {
463
- size: this.#definitions.size,
464
- keys: Array.from(this.#definitions.keys()),
465
- entries: Array.from(this.#definitions.entries()).map(([key, value]) => ({
466
- key,
467
- stepNames: Object.keys(value)
468
- }))
469
- });
461
+ this.#definitions.set(name, definition);
470
462
 
471
463
  return name;
472
464
  }
473
465
 
474
466
  /**
475
467
  * Start a new steps instance
476
- * @param {string} name - Name of the steps workflow to start
477
468
  * @param {Object} initialState - Initial state for the steps instance
478
469
  * @returns {Promise<{id: string}>} The steps instance ID
479
470
  * @throws {Error} If starting steps fails
480
471
  */
481
- async start(name, initialState) {
482
- this.emit('workflowStarted', { name, initialState });
483
-
484
- // Log definitions Map state at start
485
- console.debug('At workflow start - Current definitions Map:', {
486
- size: this.#definitions.size,
487
- keys: Array.from(this.#definitions.keys()),
488
- entries: Array.from(this.#definitions.entries()).map(([key, value]) => ({
489
- key,
490
- stepNames: Object.keys(value)
491
- }))
492
- });
472
+ async start(initialState) {
473
+ this.emit('workflowStarted', { name: this.#name, initialState });
493
474
 
494
475
  return new Promise(async (resolve, reject) => {
495
476
  try {
496
- console.debug('Starting workflow', name);
497
- const funcs = this.#definitions.get(name);
498
- console.debug('Retrieved workflow definition:', {
499
- exists: !!funcs,
500
- stepNames: funcs ? Object.keys(funcs) : []
501
- });
502
-
477
+ console.debug('Starting workflow', this.#name);
478
+ const funcs = this.#definitions.get(this.#name);
479
+
503
480
  if (!funcs) {
504
- reject(new Error(`No workflow definition found for: ${name}`));
481
+ reject(new Error(`No workflow definition found for: ${this.#name}`));
505
482
  return;
506
483
  }
507
484
 
508
485
  const firstStepName = Object.keys(funcs)[0];
509
486
  const firstStep = funcs[firstStepName];
510
- console.debug('First step details:', {
511
- name: firstStepName,
512
- exists: !!firstStep,
513
- type: firstStep ? typeof firstStep : 'undefined'
514
- });
515
-
487
+
516
488
  if (!firstStep) {
517
489
  reject(new Error('No start step defined in workflow'));
518
490
  return;
@@ -521,9 +493,9 @@ class Workflow extends EventEmitter {
521
493
  const connection = await Datastore.open();
522
494
  // Create a new workflow state in the database
523
495
  const newState = await connection.insertOne(this.#collectionName,
524
- { ...initialState, nextStep: firstStepName, createdAt: new Date().toISOString(), workflowName: name, stepCount: { } });
525
- const { _id: ID } = await connection.enqueue(`${this.#queuePrefix}_${name}_${firstStepName}`, {
526
- stepsName: name,
496
+ { ...initialState, nextStep: firstStepName, createdAt: new Date().toISOString(), workflowName: this.#name, stepCount: { } });
497
+ const { _id: ID } = await connection.enqueue(`${this.#queuePrefix}_${this.#name}_${firstStepName}`, {
498
+ stepsName: this.#name,
527
499
  goto: firstStepName,
528
500
  state: newState,
529
501
  instanceId: newState._id,
@@ -539,21 +511,20 @@ class Workflow extends EventEmitter {
539
511
 
540
512
  /**
541
513
  * Update the state of a workflow instance
542
- * @param {string} stepsName - Name of the workflow
543
514
  * @param {string} instanceId - ID of the workflow instance
544
515
  * @param {Object} state - New state to update with
545
516
  * @param {Object} options - Options for the update, { continue: false } to avoid continuing the the step
546
517
  * @returns {Promise<Object>} The updated state
547
518
  */
548
- async updateState(stepsName, instanceId, state, options={continue: true}) {
549
- this.emit('stepsStateUpdating', { stepsName, instanceId, state });
519
+ async updateState(instanceId, state, options={continue: true}) {
520
+ this.emit('stepsStateUpdating', { name: this.#name, instanceId, state });
550
521
  const connection = await Datastore.open();
551
522
  return new Promise(async (resolve, reject) => {
552
523
  const doc = await connection.updateOne(this.#collectionName,
553
524
  { _id: instanceId },
554
525
  { $set: { ...state, updatedAt: new Date().toISOString() } });
555
526
  if (options.continue) {
556
- await this.continue(stepsName, instanceId, false);
527
+ await this.continue(instanceId, false);
557
528
  }
558
529
  resolve({ ...doc });
559
530
  });
@@ -572,12 +543,11 @@ class Workflow extends EventEmitter {
572
543
 
573
544
  /**
574
545
  * Continue a paused steps instance
575
- * @param {string} stepsName - Name of the steps workflow
576
546
  * @param {string} instanceId - ID of the steps instance
577
547
  * @returns {Promise<{qId: string}>} Queue ID for the continued step
578
548
  * @throws {Error} If steps instance not found
579
549
  */
580
- async continue(stepsName, instanceId, reset=false) {
550
+ async continue(instanceId, reset=false) {
581
551
  const connection = await Datastore.open();
582
552
  const state = await connection.findOne(this.#collectionName, { _id: instanceId });
583
553
  if (!state) {
@@ -596,11 +566,11 @@ class Workflow extends EventEmitter {
596
566
 
597
567
  await connection.updateOne(this.#collectionName, { _id: instanceId }, { $set: { stepCount: state.stepCount } });
598
568
  console.debug('continue state', state);
599
- this.emit('workflowContinued', { stepsName, step: state.nextStep, instanceId });
569
+ this.emit('workflowContinued', { name: this.#name, step: state.nextStep, instanceId });
600
570
 
601
571
  return new Promise(async (resolve, reject) => {
602
- const { _id: ID } = await connection.enqueue(`${this.#queuePrefix}_${stepsName}_${state.nextStep}`, {
603
- stepsName,
572
+ const { _id: ID } = await connection.enqueue(`${this.#queuePrefix}_${this.#name}_${state.nextStep}`, {
573
+ stepsName: this.#name,
604
574
  goto: state.nextStep,
605
575
  state: state,
606
576
  options: {},
@@ -626,7 +596,7 @@ class Workflow extends EventEmitter {
626
596
  if (diffMillis > this.#timeout) {
627
597
  const diffMinutes = diffMillis / (1000 * 60);
628
598
  console.log('Timed out:', workflow._id, workflow.nextStep, `(${diffMinutes.toFixed(1)} minutes old)`);
629
- const result = await this.continue(workflow.workflowName, workflow._id, true);
599
+ const result = await this.continue(workflow._id, true);
630
600
  console.log('Continued:', result._id);
631
601
  results.push(result);
632
602
  }