@objectstack/service-automation 4.0.3 → 4.0.5
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 +437 -0
- package/dist/index.cjs +296 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -7
- package/dist/index.d.ts +41 -7
- package/dist/index.js +301 -20
- package/dist/index.js.map +1 -1
- package/package.json +32 -6
- package/.turbo/turbo-build.log +0 -22
- package/CHANGELOG.md +0 -160
- package/src/engine.test.ts +0 -1425
- package/src/engine.ts +0 -807
- package/src/index.ts +0 -14
- package/src/plugin.ts +0 -80
- package/src/plugins/crud-nodes-plugin.ts +0 -68
- package/src/plugins/http-connector-plugin.ts +0 -70
- package/src/plugins/logic-nodes-plugin.ts +0 -67
- package/tsconfig.json +0 -17
package/README.md
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
# @objectstack/service-automation
|
|
2
|
+
|
|
3
|
+
Automation Service for ObjectStack — implements `IAutomationService` with plugin-based DAG (Directed Acyclic Graph) flow execution engine.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Flow Execution Engine**: Execute multi-step automation flows with conditional logic
|
|
8
|
+
- **DAG-based Architecture**: Flows are represented as directed acyclic graphs for parallel execution
|
|
9
|
+
- **Trigger System**: Launch flows automatically on record changes, schedule, or manual invocation
|
|
10
|
+
- **Variable Management**: Pass data between flow steps with type-safe variables
|
|
11
|
+
- **Error Handling**: Built-in retry logic, error branches, and rollback support
|
|
12
|
+
- **Visual Flow Builder**: Compatible with Studio's visual flow designer
|
|
13
|
+
- **Type-Safe**: Full TypeScript support with flow definition validation
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @objectstack/service-automation
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Basic Usage
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { defineStack, defineFlow } from '@objectstack/spec';
|
|
25
|
+
import { ServiceAutomation } from '@objectstack/service-automation';
|
|
26
|
+
|
|
27
|
+
const stack = defineStack({
|
|
28
|
+
services: [ServiceAutomation.configure()],
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Flow Types
|
|
33
|
+
|
|
34
|
+
ObjectStack supports three types of flows:
|
|
35
|
+
|
|
36
|
+
### 1. Autolaunched Flows
|
|
37
|
+
Triggered automatically by record changes:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
const autoFlow = defineFlow({
|
|
41
|
+
name: 'welcome_email',
|
|
42
|
+
type: 'autolaunched',
|
|
43
|
+
trigger: {
|
|
44
|
+
object: 'user',
|
|
45
|
+
when: 'after_insert',
|
|
46
|
+
},
|
|
47
|
+
steps: [
|
|
48
|
+
{
|
|
49
|
+
type: 'action',
|
|
50
|
+
action: 'send_email',
|
|
51
|
+
inputs: {
|
|
52
|
+
to: '{!trigger.record.email}',
|
|
53
|
+
subject: 'Welcome to ObjectStack!',
|
|
54
|
+
body: 'Hello {!trigger.record.name}...',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 2. Screen Flows
|
|
62
|
+
Interactive flows with user input:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
const screenFlow = defineFlow({
|
|
66
|
+
name: 'create_opportunity',
|
|
67
|
+
type: 'screen',
|
|
68
|
+
steps: [
|
|
69
|
+
{
|
|
70
|
+
type: 'screen',
|
|
71
|
+
fields: [
|
|
72
|
+
{ name: 'account_id', label: 'Account', type: 'lookup', object: 'account' },
|
|
73
|
+
{ name: 'amount', label: 'Amount', type: 'currency' },
|
|
74
|
+
{ name: 'close_date', label: 'Close Date', type: 'date' },
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: 'record_create',
|
|
79
|
+
object: 'opportunity',
|
|
80
|
+
fields: {
|
|
81
|
+
account_id: '{!screen.account_id}',
|
|
82
|
+
amount: '{!screen.amount}',
|
|
83
|
+
close_date: '{!screen.close_date}',
|
|
84
|
+
stage: 'prospecting',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 3. Scheduled Flows
|
|
92
|
+
Run on a schedule (cron syntax):
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const scheduledFlow = defineFlow({
|
|
96
|
+
name: 'daily_report',
|
|
97
|
+
type: 'scheduled',
|
|
98
|
+
schedule: '0 9 * * *', // Every day at 9 AM
|
|
99
|
+
steps: [
|
|
100
|
+
{
|
|
101
|
+
type: 'query',
|
|
102
|
+
object: 'order',
|
|
103
|
+
filters: [
|
|
104
|
+
{ field: 'created_at', operator: 'yesterday' },
|
|
105
|
+
],
|
|
106
|
+
output: 'orders',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
type: 'action',
|
|
110
|
+
action: 'send_email',
|
|
111
|
+
inputs: {
|
|
112
|
+
to: 'admin@company.com',
|
|
113
|
+
subject: 'Daily Orders Report',
|
|
114
|
+
body: 'Total orders: {!orders.length}',
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Flow Steps
|
|
122
|
+
|
|
123
|
+
### Record Operations
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// Create record
|
|
127
|
+
{
|
|
128
|
+
type: 'record_create',
|
|
129
|
+
object: 'contact',
|
|
130
|
+
fields: {
|
|
131
|
+
name: '{!input.name}',
|
|
132
|
+
email: '{!input.email}',
|
|
133
|
+
},
|
|
134
|
+
output: 'new_contact',
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Update record
|
|
138
|
+
{
|
|
139
|
+
type: 'record_update',
|
|
140
|
+
object: 'account',
|
|
141
|
+
recordId: '{!trigger.recordId}',
|
|
142
|
+
fields: {
|
|
143
|
+
status: 'active',
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Delete record
|
|
148
|
+
{
|
|
149
|
+
type: 'record_delete',
|
|
150
|
+
object: 'task',
|
|
151
|
+
recordId: '{!input.taskId}',
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Query Step
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
{
|
|
159
|
+
type: 'query',
|
|
160
|
+
object: 'opportunity',
|
|
161
|
+
filters: [
|
|
162
|
+
{ field: 'account_id', operator: 'eq', value: '{!trigger.record.account_id}' },
|
|
163
|
+
{ field: 'stage', operator: 'eq', value: 'closed_won' },
|
|
164
|
+
],
|
|
165
|
+
sort: [{ field: 'amount', direction: 'desc' }],
|
|
166
|
+
limit: 10,
|
|
167
|
+
output: 'opportunities',
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Decision (Conditional) Step
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
{
|
|
175
|
+
type: 'decision',
|
|
176
|
+
conditions: [
|
|
177
|
+
{
|
|
178
|
+
label: 'High Value',
|
|
179
|
+
expression: '{!trigger.record.amount} > 10000',
|
|
180
|
+
steps: [
|
|
181
|
+
{ type: 'action', action: 'notify_sales_manager' },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
label: 'Medium Value',
|
|
186
|
+
expression: '{!trigger.record.amount} > 1000',
|
|
187
|
+
steps: [
|
|
188
|
+
{ type: 'action', action: 'assign_to_sales_rep' },
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
defaultSteps: [
|
|
193
|
+
{ type: 'action', action: 'auto_approve' },
|
|
194
|
+
],
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Loop Step
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
{
|
|
202
|
+
type: 'loop',
|
|
203
|
+
collection: '{!query_results}',
|
|
204
|
+
variable: 'item',
|
|
205
|
+
steps: [
|
|
206
|
+
{
|
|
207
|
+
type: 'record_update',
|
|
208
|
+
object: 'task',
|
|
209
|
+
recordId: '{!item.id}',
|
|
210
|
+
fields: {
|
|
211
|
+
status: 'completed',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Custom Action Step
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
{
|
|
222
|
+
type: 'action',
|
|
223
|
+
action: 'calculate_tax',
|
|
224
|
+
inputs: {
|
|
225
|
+
amount: '{!opportunity.amount}',
|
|
226
|
+
region: '{!account.billing_region}',
|
|
227
|
+
},
|
|
228
|
+
output: 'tax_amount',
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Variable Expressions
|
|
233
|
+
|
|
234
|
+
Access variables in flow steps using `{!variable.path}` syntax:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// Trigger record fields
|
|
238
|
+
'{!trigger.record.name}'
|
|
239
|
+
'{!trigger.record.account.industry}'
|
|
240
|
+
|
|
241
|
+
// Screen input
|
|
242
|
+
'{!screen.fieldName}'
|
|
243
|
+
|
|
244
|
+
// Query results
|
|
245
|
+
'{!query_results[0].name}'
|
|
246
|
+
'{!query_results.length}'
|
|
247
|
+
|
|
248
|
+
// Step outputs
|
|
249
|
+
'{!step_name.output_field}'
|
|
250
|
+
|
|
251
|
+
// System variables
|
|
252
|
+
'{!now}'
|
|
253
|
+
'{!today}'
|
|
254
|
+
'{!currentUser.id}'
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Service API
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// Get automation service
|
|
261
|
+
const automation = kernel.getService<IAutomationService>('automation');
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Execute Flow
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
// Execute a flow manually
|
|
268
|
+
const result = await automation.executeFlow({
|
|
269
|
+
flowName: 'create_opportunity',
|
|
270
|
+
inputs: {
|
|
271
|
+
account_id: '123',
|
|
272
|
+
amount: 50000,
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Check execution status
|
|
277
|
+
if (result.status === 'success') {
|
|
278
|
+
console.log('Flow completed:', result.outputs);
|
|
279
|
+
} else {
|
|
280
|
+
console.error('Flow failed:', result.error);
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Flow Management
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// Get flow definition
|
|
288
|
+
const flow = await automation.getFlow('welcome_email');
|
|
289
|
+
|
|
290
|
+
// List all flows
|
|
291
|
+
const flows = await automation.listFlows();
|
|
292
|
+
|
|
293
|
+
// Get flow execution history
|
|
294
|
+
const history = await automation.getFlowHistory({
|
|
295
|
+
flowName: 'daily_report',
|
|
296
|
+
limit: 100,
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Trigger Management
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// Register a custom trigger
|
|
304
|
+
automation.registerTrigger({
|
|
305
|
+
name: 'on_payment_received',
|
|
306
|
+
description: 'Triggered when a payment is received',
|
|
307
|
+
async handler(context) {
|
|
308
|
+
// Trigger logic
|
|
309
|
+
return {
|
|
310
|
+
record: context.payment,
|
|
311
|
+
timestamp: new Date(),
|
|
312
|
+
};
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## REST API Endpoints
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
POST /api/v1/automation/flows/:name/execute # Execute flow
|
|
321
|
+
GET /api/v1/automation/flows # List flows
|
|
322
|
+
GET /api/v1/automation/flows/:name # Get flow definition
|
|
323
|
+
GET /api/v1/automation/flows/:name/history # Get execution history
|
|
324
|
+
POST /api/v1/automation/triggers/:name # Trigger a flow
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Advanced Features
|
|
328
|
+
|
|
329
|
+
### Parallel Execution
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
const flow = defineFlow({
|
|
333
|
+
name: 'parallel_processing',
|
|
334
|
+
steps: [
|
|
335
|
+
{
|
|
336
|
+
type: 'parallel',
|
|
337
|
+
branches: [
|
|
338
|
+
{
|
|
339
|
+
name: 'branch1',
|
|
340
|
+
steps: [{ type: 'action', action: 'process_a' }],
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: 'branch2',
|
|
344
|
+
steps: [{ type: 'action', action: 'process_b' }],
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
});
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Error Handling
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
{
|
|
356
|
+
type: 'try_catch',
|
|
357
|
+
trySteps: [
|
|
358
|
+
{ type: 'action', action: 'risky_operation' },
|
|
359
|
+
],
|
|
360
|
+
catchSteps: [
|
|
361
|
+
{
|
|
362
|
+
type: 'action',
|
|
363
|
+
action: 'send_error_notification',
|
|
364
|
+
inputs: {
|
|
365
|
+
error: '{!error.message}',
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Subflows
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
{
|
|
376
|
+
type: 'subflow',
|
|
377
|
+
flowName: 'validate_address',
|
|
378
|
+
inputs: {
|
|
379
|
+
street: '{!input.street}',
|
|
380
|
+
city: '{!input.city}',
|
|
381
|
+
},
|
|
382
|
+
output: 'validated_address',
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Wait Step
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
{
|
|
390
|
+
type: 'wait',
|
|
391
|
+
duration: { hours: 24 },
|
|
392
|
+
nextSteps: [
|
|
393
|
+
{ type: 'action', action: 'send_reminder' },
|
|
394
|
+
],
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Best Practices
|
|
399
|
+
|
|
400
|
+
1. **Keep Flows Simple**: Break complex logic into multiple flows
|
|
401
|
+
2. **Use Descriptive Names**: Name flows and steps clearly
|
|
402
|
+
3. **Handle Errors**: Always include error handling for critical operations
|
|
403
|
+
4. **Test Thoroughly**: Test flows with various input scenarios
|
|
404
|
+
5. **Monitor Performance**: Track flow execution times and optimize slow flows
|
|
405
|
+
6. **Version Control**: Store flow definitions in version control
|
|
406
|
+
7. **Document Intent**: Add descriptions to flows and steps
|
|
407
|
+
|
|
408
|
+
## Performance Considerations
|
|
409
|
+
|
|
410
|
+
- **Parallel Execution**: DAG engine automatically parallelizes independent steps
|
|
411
|
+
- **Batch Processing**: Use loop steps efficiently for large collections
|
|
412
|
+
- **Query Optimization**: Filter queries early to reduce data volume
|
|
413
|
+
- **Async Execution**: Long-running flows execute asynchronously
|
|
414
|
+
|
|
415
|
+
## Contract Implementation
|
|
416
|
+
|
|
417
|
+
Implements `IAutomationService` from `@objectstack/spec/contracts`:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
interface IAutomationService {
|
|
421
|
+
executeFlow(options: FlowExecutionOptions): Promise<FlowResult>;
|
|
422
|
+
getFlow(name: string): Promise<Flow>;
|
|
423
|
+
listFlows(filter?: FlowFilter): Promise<Flow[]>;
|
|
424
|
+
getFlowHistory(options: FlowHistoryOptions): Promise<FlowExecution[]>;
|
|
425
|
+
registerTrigger(trigger: TriggerDefinition): void;
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## License
|
|
430
|
+
|
|
431
|
+
Apache-2.0
|
|
432
|
+
|
|
433
|
+
## See Also
|
|
434
|
+
|
|
435
|
+
- [@objectstack/spec/automation](../../spec/src/automation/)
|
|
436
|
+
- [Flow Builder Guide](/content/docs/guides/automation/)
|
|
437
|
+
- [Trigger Reference](/content/docs/references/automation/)
|