codehooks-js 1.3.11 → 1.3.13

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,199 @@ 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. **Datastore** with MongoDB-like query syntax, key-value operations, and queue management
321
+ 9. **Security & Authentication** with JWKS support and custom auth middleware for secure API endpoints
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {agg} from './aggregation/index.mjs';
2
2
  import {crudlify as crud} from './crudlify/index.mjs';
3
- import {serveStatic as ws, render as renderView} from './webserver.mjs';
3
+ import {serveStatic as ws, render as renderView, internalFetch} from './webserver.mjs';
4
4
  import Workflow from './workflow/engine.mjs';
5
5
 
6
6
  function createRoute(str) {
@@ -116,6 +116,10 @@ class Codehooks {
116
116
  res.status(200).json({...data});
117
117
  })
118
118
  }
119
+
120
+ internalFetch = (url, options = {}) => {
121
+ return internalFetch(url, options);
122
+ }
119
123
 
120
124
  createWorkflow = (name, description, steps, options={}) => {
121
125
  const wf = new Workflow(name, description, steps, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codehooks-js",
3
- "version": "1.3.11",
3
+ "version": "1.3.13",
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
@@ -1048,6 +1048,13 @@ export class Codehooks {
1048
1048
  * @returns Promise with the registered workflow name
1049
1049
  */
1050
1050
  registerWorkflow: (name: string, description: string, steps: WorkflowDefinition) => Promise<string>;
1051
+ /**
1052
+ * Fetch data from another Codehooks API
1053
+ * @param url - URL to fetch from, e.g. http://myapi-ffee.codehooks.io/dev/api/myroute
1054
+ * @param options - Fetch options
1055
+ * @returns Promise with the fetched data
1056
+ */
1057
+ internalFetch: (url: string, options?: any) => Promise<any>;
1051
1058
  }
1052
1059
  declare const _coho: Codehooks;
1053
1060
 
@@ -1080,10 +1087,10 @@ export type WorkflowEvents = {
1080
1087
  'stateUpdated': { workflowName: string; state: any; instanceId: string };
1081
1088
 
1082
1089
  /**
1083
- * Emitted when a step is waiting for input
1090
+ * Emitted when a step is enqueued for execution
1084
1091
  * @event
1085
1092
  */
1086
- 'stepWaiting': { workflowName: string; step: string; instanceId: string };
1093
+ 'stepEnqueued': { workflowName: string; step: string; state: any; instanceId: string };
1087
1094
 
1088
1095
  /**
1089
1096
  * Emitted when a workflow instance is continued after waiting
@@ -1145,20 +1152,6 @@ export type WorkflowConfig = {
1145
1152
  steps?: Record<string, StepOptions>;
1146
1153
  };
1147
1154
 
1148
- /**
1149
- * Engine for managing workflow-based applications
1150
- * @extends Workflow
1151
- */
1152
- export type WorkflowEngine = Workflow & {
1153
- /**
1154
- * Register a new workflow
1155
- * @param name - Unique identifier for the workflow
1156
- * @param description - Human-readable description
1157
- * @param steps - Step definitions
1158
- */
1159
- register: (name: string, description: string, steps: WorkflowDefinition) => Promise<string>;
1160
- };
1161
-
1162
1155
  /**
1163
1156
  * Workflow engine for managing step-based applications
1164
1157
  * @extends EventEmitter
@@ -1189,48 +1182,118 @@ export type Workflow = {
1189
1182
  configure: (options: WorkflowConfig) => void;
1190
1183
 
1191
1184
  /**
1192
- * Register a new workflow
1185
+ * Get the collection name for storing workflow data
1186
+ * @returns The collection name
1187
+ */
1188
+ getCollectionName: () => string;
1189
+
1190
+ /**
1191
+ * Set the collection name for storing workflow data
1192
+ * @param name - Collection name
1193
+ */
1194
+ setCollectionName: (name: string) => void;
1195
+
1196
+ /**
1197
+ * Get the queue prefix for workflow jobs
1198
+ * @returns The queue prefix
1199
+ */
1200
+ getQueuePrefix: () => string;
1201
+
1202
+ /**
1203
+ * Set the queue prefix for workflow jobs
1204
+ * @param prefix - Queue prefix
1205
+ */
1206
+ setQueuePrefix: (prefix: string) => void;
1207
+
1208
+ /**
1209
+ * Get the timeout for workflow steps
1210
+ * @returns The timeout in milliseconds
1211
+ */
1212
+ getTimeout: () => number;
1213
+
1214
+ /**
1215
+ * Set the timeout for workflow steps
1216
+ * @param timeout - Timeout in milliseconds
1217
+ */
1218
+ setTimeout: (timeout: number) => void;
1219
+
1220
+ /**
1221
+ * Get the maximum step count
1222
+ * @returns The maximum step count
1223
+ */
1224
+ getMaxStepCount: () => number;
1225
+
1226
+ /**
1227
+ * Set the maximum step count
1228
+ * @param maxStepCount - Maximum step count
1229
+ */
1230
+ setMaxStepCount: (maxStepCount: number) => void;
1231
+
1232
+ /**
1233
+ * Get the steps configuration
1234
+ * @returns The steps configuration
1235
+ */
1236
+ getStepsConfig: () => Record<string, StepOptions>;
1237
+
1238
+ /**
1239
+ * Set the steps configuration
1240
+ * @param steps - Steps configuration
1241
+ */
1242
+ setStepsConfig: (steps: Record<string, StepOptions>) => void;
1243
+
1244
+ /**
1245
+ * Get the step definition for a specific step
1246
+ * @param workflowName - Name of the workflow
1247
+ * @param stepName - Name of the step
1248
+ * @returns The step function
1249
+ */
1250
+ getDefinition: (workflowName: string, stepName: string) => Function;
1251
+
1252
+ /**
1253
+ * Register a new workflow with a Codehooks application
1193
1254
  * @param app - Codehooks application instance
1194
- * @param name - Unique identifier for the workflow
1195
- * @param description - Human-readable description
1196
- * @param definition - Object containing step definitions
1197
1255
  * @returns Promise with the registered workflow name
1198
1256
  */
1199
- register: (app: Codehooks, name: string, description: string, definition: Record<string, Function>) => Promise<string>;
1257
+ register: (app: Codehooks) => Promise<string>;
1200
1258
 
1201
1259
  /**
1202
1260
  * Start a new workflow instance
1203
- * @param name - Name of the workflow to start
1204
1261
  * @param initialState - Initial state for the workflow instance
1205
- * @returns Promise with the workflow instance ID
1262
+ * @returns Promise with the workflow instance
1206
1263
  */
1207
- start: (name: string, initialState: any) => Promise<{ id: string }>;
1264
+ start: (initialState: any) => Promise<any>;
1208
1265
 
1209
1266
  /**
1210
1267
  * Update the state of a workflow instance
1211
- * @param workflowName - Name of the workflow
1212
1268
  * @param instanceId - ID of the workflow instance
1213
1269
  * @param state - New state to update with
1214
1270
  * @param options - Options for the update, { continue: false } to avoid continuing the step
1215
1271
  * @returns Promise with the updated state
1216
1272
  */
1217
- updateState: (workflowName: string, instanceId: string, state: any, options?: UpdateOptions) => Promise<any>;
1273
+ updateState: (instanceId: string, state: any, options?: UpdateOptions) => Promise<any>;
1274
+
1275
+ /**
1276
+ * Set the complete state of a workflow instance
1277
+ * @param instanceId - ID of the workflow instance
1278
+ * @param stateData - Object containing _id and state
1279
+ * @returns Promise<void>
1280
+ */
1281
+ setState: (instanceId: string, stateData: { _id: string; state: any }) => Promise<void>;
1218
1282
 
1219
1283
  /**
1220
1284
  * Continue a paused workflow instance
1221
- * @param workflowName - Name of the workflow
1222
1285
  * @param instanceId - ID of the workflow instance
1223
1286
  * @param reset - Whether to reset all step counts (true) or just the current step (false)
1224
1287
  * @returns Promise with the queue ID for the continued step
1225
1288
  */
1226
- continue: (workflowName: string, instanceId: string, reset?: boolean) => Promise<{ qId: string }>;
1289
+ continue: (instanceId: string, reset?: boolean) => Promise<{ instanceId: string }>;
1227
1290
 
1228
1291
  /**
1229
1292
  * Get the status of a workflow instance
1230
1293
  * @param id - ID of the workflow instance
1231
1294
  * @returns Promise with the workflow status
1232
1295
  */
1233
- getWorkflowStatus: (id: string) => Promise<any>;
1296
+ getStepsStatus: (id: string) => Promise<any>;
1234
1297
 
1235
1298
  /**
1236
1299
  * Get all workflow instances matching a filter
@@ -1244,7 +1307,7 @@ export type Workflow = {
1244
1307
  * @param id - ID of the workflow instance to cancel
1245
1308
  * @returns Promise with the cancellation result
1246
1309
  */
1247
- cancelWorkflow: (id: string) => Promise<any>;
1310
+ cancelSteps: (id: string) => Promise<any>;
1248
1311
 
1249
1312
  /**
1250
1313
  * Register an event listener
@@ -1282,7 +1345,7 @@ export type Workflow = {
1282
1345
  * Continue all timed out workflow instances
1283
1346
  * @returns Promise with array of results containing queue IDs for continued workflows
1284
1347
  */
1285
- continueAllTimedOut: () => Promise<Array<{qId: string}>>;
1348
+ continueAllTimedOut: () => Promise<Array<{instanceId: string}>>;
1286
1349
 
1287
1350
  /**
1288
1351
  * Check if a specific step in a workflow instance has timed out
@@ -1301,6 +1364,7 @@ export type Workflow = {
1301
1364
  * // finishTime?: string, // if step has finished
1302
1365
  * // currentTime?: string, // if step is still running
1303
1366
  * // timeoutSource: 'stepConfig' | 'defaultOptions' | 'globalTimeout'
1367
+ * // reason?: string // if step not found
1304
1368
  * // }
1305
1369
  */
1306
1370
  isStepTimedOut: (workflow: any) => {
@@ -1378,11 +1442,14 @@ export type WorkflowEvent =
1378
1442
  * Data structure for workflow events
1379
1443
  */
1380
1444
  export type WorkflowEventData = {
1381
- workflowName: string;
1445
+ workflowName?: string;
1446
+ name?: string;
1447
+ description?: string;
1382
1448
  step?: string;
1383
1449
  state?: any;
1384
1450
  instanceId?: string;
1385
1451
  error?: Error;
1452
+ id?: string;
1386
1453
  [key: string]: any;
1387
1454
  };
1388
1455
 
package/webserver.mjs CHANGED
@@ -1,4 +1,6 @@
1
1
  import mime from 'mime';
2
+ import fetch from 'node-fetch';
3
+ import { URL } from 'url';
2
4
 
3
5
  // old and new style support
4
6
  function promisify(cb, fn) {
@@ -222,4 +224,30 @@ async function handlebars(viewFile, data, settings, cb) {
222
224
  console.error('Handle bars engine error:', err)
223
225
  cb(err);
224
226
  }
225
- }
227
+ }
228
+
229
+ // Generic fetch wrapper that routes through apigateway-service
230
+ export const internalFetch = (url, options = {}) => {
231
+ // Parse the original URL to extract domain and path
232
+
233
+ const urlObj = new URL(url);
234
+ const originalHost = urlObj.host;
235
+ const pathAndQuery = urlObj.pathname + urlObj.search;
236
+
237
+ // Replace domain with apigateway-service
238
+ const proxyUrl = `http://apigateway-service:8888${pathAndQuery}`;
239
+
240
+ // Merge headers with original host
241
+ const headers = {
242
+ ...options.headers,
243
+ 'host': originalHost
244
+ };
245
+
246
+ // Create new options with updated headers
247
+ const newOptions = {
248
+ ...options,
249
+ headers
250
+ };
251
+
252
+ return fetch(proxyUrl, newOptions);
253
+ };
@@ -423,17 +423,7 @@ class Workflow extends EventEmitter {
423
423
  * @private
424
424
  */
425
425
  async registerWithApp(app, name, description, definition) {
426
- this.emit('workflowCreated', { name, description });
427
-
428
- // Log initial state of definitions Map
429
- console.debug('Before registration - Current definitions Map:', {
430
- size: this.#definitions.size,
431
- keys: Array.from(this.#definitions.keys()),
432
- entries: Array.from(this.#definitions.entries()).map(([key, value]) => ({
433
- key,
434
- stepNames: Object.keys(value)
435
- }))
436
- });
426
+ this.emit('workflowCreated', { name, description });
437
427
 
438
428
  // Validate each step in the definition
439
429
  for (const [stepName, step] of Object.entries(definition)) {
@@ -468,63 +458,33 @@ class Workflow extends EventEmitter {
468
458
  }
469
459
 
470
460
  // Store the definition
471
- this.#definitions.set(name, definition);
472
-
473
- // Log state after registration
474
- console.debug('After registration - Updated definitions Map:', {
475
- size: this.#definitions.size,
476
- keys: Array.from(this.#definitions.keys()),
477
- entries: Array.from(this.#definitions.entries()).map(([key, value]) => ({
478
- key,
479
- stepNames: Object.keys(value)
480
- }))
481
- });
461
+ this.#definitions.set(name, definition);
482
462
 
483
463
  return name;
484
464
  }
485
465
 
486
466
  /**
487
467
  * Start a new steps instance
488
- * @param {string} name - Name of the steps workflow to start
489
468
  * @param {Object} initialState - Initial state for the steps instance
490
469
  * @returns {Promise<{id: string}>} The steps instance ID
491
470
  * @throws {Error} If starting steps fails
492
471
  */
493
- async start(name, initialState) {
494
- this.emit('workflowStarted', { name, initialState });
495
-
496
- // Log definitions Map state at start
497
- console.debug('At workflow start - Current definitions Map:', {
498
- size: this.#definitions.size,
499
- keys: Array.from(this.#definitions.keys()),
500
- entries: Array.from(this.#definitions.entries()).map(([key, value]) => ({
501
- key,
502
- stepNames: Object.keys(value)
503
- }))
504
- });
472
+ async start(initialState) {
473
+ this.emit('workflowStarted', { name: this.#name, initialState });
505
474
 
506
475
  return new Promise(async (resolve, reject) => {
507
476
  try {
508
- console.debug('Starting workflow', name);
509
- const funcs = this.#definitions.get(name);
510
- console.debug('Retrieved workflow definition:', {
511
- exists: !!funcs,
512
- stepNames: funcs ? Object.keys(funcs) : []
513
- });
514
-
477
+ console.debug('Starting workflow', this.#name);
478
+ const funcs = this.#definitions.get(this.#name);
479
+
515
480
  if (!funcs) {
516
- reject(new Error(`No workflow definition found for: ${name}`));
481
+ reject(new Error(`No workflow definition found for: ${this.#name}`));
517
482
  return;
518
483
  }
519
484
 
520
485
  const firstStepName = Object.keys(funcs)[0];
521
486
  const firstStep = funcs[firstStepName];
522
- console.debug('First step details:', {
523
- name: firstStepName,
524
- exists: !!firstStep,
525
- type: firstStep ? typeof firstStep : 'undefined'
526
- });
527
-
487
+
528
488
  if (!firstStep) {
529
489
  reject(new Error('No start step defined in workflow'));
530
490
  return;
@@ -533,9 +493,9 @@ class Workflow extends EventEmitter {
533
493
  const connection = await Datastore.open();
534
494
  // Create a new workflow state in the database
535
495
  const newState = await connection.insertOne(this.#collectionName,
536
- { ...initialState, nextStep: firstStepName, createdAt: new Date().toISOString(), workflowName: name, stepCount: { } });
537
- const { _id: ID } = await connection.enqueue(`${this.#queuePrefix}_${name}_${firstStepName}`, {
538
- 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,
539
499
  goto: firstStepName,
540
500
  state: newState,
541
501
  instanceId: newState._id,
@@ -551,21 +511,20 @@ class Workflow extends EventEmitter {
551
511
 
552
512
  /**
553
513
  * Update the state of a workflow instance
554
- * @param {string} stepsName - Name of the workflow
555
514
  * @param {string} instanceId - ID of the workflow instance
556
515
  * @param {Object} state - New state to update with
557
516
  * @param {Object} options - Options for the update, { continue: false } to avoid continuing the the step
558
517
  * @returns {Promise<Object>} The updated state
559
518
  */
560
- async updateState(stepsName, instanceId, state, options={continue: true}) {
561
- this.emit('stepsStateUpdating', { stepsName, instanceId, state });
519
+ async updateState(instanceId, state, options={continue: true}) {
520
+ this.emit('stepsStateUpdating', { name: this.#name, instanceId, state });
562
521
  const connection = await Datastore.open();
563
522
  return new Promise(async (resolve, reject) => {
564
523
  const doc = await connection.updateOne(this.#collectionName,
565
524
  { _id: instanceId },
566
525
  { $set: { ...state, updatedAt: new Date().toISOString() } });
567
526
  if (options.continue) {
568
- await this.continue(stepsName, instanceId, false);
527
+ await this.continue(instanceId, false);
569
528
  }
570
529
  resolve({ ...doc });
571
530
  });
@@ -584,12 +543,11 @@ class Workflow extends EventEmitter {
584
543
 
585
544
  /**
586
545
  * Continue a paused steps instance
587
- * @param {string} stepsName - Name of the steps workflow
588
546
  * @param {string} instanceId - ID of the steps instance
589
547
  * @returns {Promise<{qId: string}>} Queue ID for the continued step
590
548
  * @throws {Error} If steps instance not found
591
549
  */
592
- async continue(stepsName, instanceId, reset=false) {
550
+ async continue(instanceId, reset=false) {
593
551
  const connection = await Datastore.open();
594
552
  const state = await connection.findOne(this.#collectionName, { _id: instanceId });
595
553
  if (!state) {
@@ -608,11 +566,11 @@ class Workflow extends EventEmitter {
608
566
 
609
567
  await connection.updateOne(this.#collectionName, { _id: instanceId }, { $set: { stepCount: state.stepCount } });
610
568
  console.debug('continue state', state);
611
- this.emit('workflowContinued', { stepsName, step: state.nextStep, instanceId });
569
+ this.emit('workflowContinued', { name: this.#name, step: state.nextStep, instanceId });
612
570
 
613
571
  return new Promise(async (resolve, reject) => {
614
- const { _id: ID } = await connection.enqueue(`${this.#queuePrefix}_${stepsName}_${state.nextStep}`, {
615
- stepsName,
572
+ const { _id: ID } = await connection.enqueue(`${this.#queuePrefix}_${this.#name}_${state.nextStep}`, {
573
+ stepsName: this.#name,
616
574
  goto: state.nextStep,
617
575
  state: state,
618
576
  options: {},
@@ -638,7 +596,7 @@ class Workflow extends EventEmitter {
638
596
  if (diffMillis > this.#timeout) {
639
597
  const diffMinutes = diffMillis / (1000 * 60);
640
598
  console.log('Timed out:', workflow._id, workflow.nextStep, `(${diffMinutes.toFixed(1)} minutes old)`);
641
- const result = await this.continue(workflow.workflowName, workflow._id, true);
599
+ const result = await this.continue(workflow._id, true);
642
600
  console.log('Continued:', result._id);
643
601
  results.push(result);
644
602
  }