codehooks-js 1.3.11 → 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 +204 -7
- package/package.json +1 -1
- package/types/index.d.ts +92 -32
- package/workflow/engine.mjs +20 -62
package/README.md
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
# codehooks-js
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
5
|
+
## What is Codehooks.io?
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
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
|
|
1083
|
+
* Emitted when a step is enqueued for execution
|
|
1084
1084
|
* @event
|
|
1085
1085
|
*/
|
|
1086
|
-
'
|
|
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
|
|
@@ -1145,20 +1145,6 @@ export type WorkflowConfig = {
|
|
|
1145
1145
|
steps?: Record<string, StepOptions>;
|
|
1146
1146
|
};
|
|
1147
1147
|
|
|
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
1148
|
/**
|
|
1163
1149
|
* Workflow engine for managing step-based applications
|
|
1164
1150
|
* @extends EventEmitter
|
|
@@ -1189,48 +1175,118 @@ export type Workflow = {
|
|
|
1189
1175
|
configure: (options: WorkflowConfig) => void;
|
|
1190
1176
|
|
|
1191
1177
|
/**
|
|
1192
|
-
*
|
|
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
|
|
1193
1247
|
* @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
1248
|
* @returns Promise with the registered workflow name
|
|
1198
1249
|
*/
|
|
1199
|
-
register: (app: Codehooks
|
|
1250
|
+
register: (app: Codehooks) => Promise<string>;
|
|
1200
1251
|
|
|
1201
1252
|
/**
|
|
1202
1253
|
* Start a new workflow instance
|
|
1203
|
-
* @param name - Name of the workflow to start
|
|
1204
1254
|
* @param initialState - Initial state for the workflow instance
|
|
1205
|
-
* @returns Promise with the workflow instance
|
|
1255
|
+
* @returns Promise with the workflow instance
|
|
1206
1256
|
*/
|
|
1207
|
-
start: (
|
|
1257
|
+
start: (initialState: any) => Promise<any>;
|
|
1208
1258
|
|
|
1209
1259
|
/**
|
|
1210
1260
|
* Update the state of a workflow instance
|
|
1211
|
-
* @param workflowName - Name of the workflow
|
|
1212
1261
|
* @param instanceId - ID of the workflow instance
|
|
1213
1262
|
* @param state - New state to update with
|
|
1214
1263
|
* @param options - Options for the update, { continue: false } to avoid continuing the step
|
|
1215
1264
|
* @returns Promise with the updated state
|
|
1216
1265
|
*/
|
|
1217
|
-
updateState: (
|
|
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>;
|
|
1218
1275
|
|
|
1219
1276
|
/**
|
|
1220
1277
|
* Continue a paused workflow instance
|
|
1221
|
-
* @param workflowName - Name of the workflow
|
|
1222
1278
|
* @param instanceId - ID of the workflow instance
|
|
1223
1279
|
* @param reset - Whether to reset all step counts (true) or just the current step (false)
|
|
1224
1280
|
* @returns Promise with the queue ID for the continued step
|
|
1225
1281
|
*/
|
|
1226
|
-
continue: (
|
|
1282
|
+
continue: (instanceId: string, reset?: boolean) => Promise<{ instanceId: string }>;
|
|
1227
1283
|
|
|
1228
1284
|
/**
|
|
1229
1285
|
* Get the status of a workflow instance
|
|
1230
1286
|
* @param id - ID of the workflow instance
|
|
1231
1287
|
* @returns Promise with the workflow status
|
|
1232
1288
|
*/
|
|
1233
|
-
|
|
1289
|
+
getStepsStatus: (id: string) => Promise<any>;
|
|
1234
1290
|
|
|
1235
1291
|
/**
|
|
1236
1292
|
* Get all workflow instances matching a filter
|
|
@@ -1244,7 +1300,7 @@ export type Workflow = {
|
|
|
1244
1300
|
* @param id - ID of the workflow instance to cancel
|
|
1245
1301
|
* @returns Promise with the cancellation result
|
|
1246
1302
|
*/
|
|
1247
|
-
|
|
1303
|
+
cancelSteps: (id: string) => Promise<any>;
|
|
1248
1304
|
|
|
1249
1305
|
/**
|
|
1250
1306
|
* Register an event listener
|
|
@@ -1282,7 +1338,7 @@ export type Workflow = {
|
|
|
1282
1338
|
* Continue all timed out workflow instances
|
|
1283
1339
|
* @returns Promise with array of results containing queue IDs for continued workflows
|
|
1284
1340
|
*/
|
|
1285
|
-
continueAllTimedOut: () => Promise<Array<{
|
|
1341
|
+
continueAllTimedOut: () => Promise<Array<{instanceId: string}>>;
|
|
1286
1342
|
|
|
1287
1343
|
/**
|
|
1288
1344
|
* Check if a specific step in a workflow instance has timed out
|
|
@@ -1301,6 +1357,7 @@ export type Workflow = {
|
|
|
1301
1357
|
* // finishTime?: string, // if step has finished
|
|
1302
1358
|
* // currentTime?: string, // if step is still running
|
|
1303
1359
|
* // timeoutSource: 'stepConfig' | 'defaultOptions' | 'globalTimeout'
|
|
1360
|
+
* // reason?: string // if step not found
|
|
1304
1361
|
* // }
|
|
1305
1362
|
*/
|
|
1306
1363
|
isStepTimedOut: (workflow: any) => {
|
|
@@ -1378,11 +1435,14 @@ export type WorkflowEvent =
|
|
|
1378
1435
|
* Data structure for workflow events
|
|
1379
1436
|
*/
|
|
1380
1437
|
export type WorkflowEventData = {
|
|
1381
|
-
workflowName
|
|
1438
|
+
workflowName?: string;
|
|
1439
|
+
name?: string;
|
|
1440
|
+
description?: string;
|
|
1382
1441
|
step?: string;
|
|
1383
1442
|
state?: any;
|
|
1384
1443
|
instanceId?: string;
|
|
1385
1444
|
error?: Error;
|
|
1445
|
+
id?: string;
|
|
1386
1446
|
[key: string]: any;
|
|
1387
1447
|
};
|
|
1388
1448
|
|
package/workflow/engine.mjs
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
561
|
-
this.emit('stepsStateUpdating', {
|
|
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(
|
|
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(
|
|
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', {
|
|
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}_${
|
|
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.
|
|
599
|
+
const result = await this.continue(workflow._id, true);
|
|
642
600
|
console.log('Continued:', result._id);
|
|
643
601
|
results.push(result);
|
|
644
602
|
}
|