@onlineapps/cookbook-core 2.1.7 → 2.1.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/cookbook-core",
3
- "version": "2.1.7",
3
+ "version": "2.1.8",
4
4
  "description": "Core cookbook parsing and validation - lightweight foundation for workflow definitions",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$comment": "COOKBOOK V2 FORMAT - V1 IS BANNED! steps MUST be object keyed by step_id, NOT array!",
3
4
  "type": "object",
4
5
  "required": ["steps", "version"],
5
6
  "additionalProperties": true,
@@ -10,8 +11,8 @@
10
11
  },
11
12
  "version": {
12
13
  "type": "string",
13
- "pattern": "^\\d+\\.\\d+\\.\\d+$",
14
- "description": "Semantic version of the cookbook schema"
14
+ "pattern": "^2\\.\\d+\\.\\d+$",
15
+ "description": "Semantic version - MUST be 2.x.x (V1 is banned)"
15
16
  },
16
17
  "description": {
17
18
  "type": "string"
@@ -34,50 +35,25 @@
34
35
  },
35
36
  "metadata": {
36
37
  "type": "object",
37
- "properties": {
38
- "tags": {
39
- "type": "array",
40
- "items": {
41
- "type": "string"
42
- }
43
- },
44
- "priority": {
45
- "type": "integer",
46
- "minimum": 0,
47
- "maximum": 10
48
- },
49
- "deprecated": {
50
- "type": "boolean"
51
- },
52
- "deprecationMessage": {
53
- "type": "string"
54
- },
55
- "author": {
56
- "type": "string"
57
- },
58
- "created": {
59
- "type": "string",
60
- "format": "date-time"
61
- },
62
- "modified": {
63
- "type": "string",
64
- "format": "date-time"
65
- }
66
- },
67
38
  "additionalProperties": true
68
39
  },
69
40
  "steps": {
70
- "type": "array",
71
- "minItems": 1,
72
- "items": {
41
+ "type": "object",
42
+ "minProperties": 1,
43
+ "additionalProperties": {
73
44
  "$ref": "#/definitions/Step"
74
- }
45
+ },
46
+ "description": "V2: Object keyed by step_id (NOT array!)"
47
+ },
48
+ "delivery": {
49
+ "type": "object",
50
+ "description": "Delivery configuration for workflow results"
75
51
  }
76
52
  },
77
53
  "definitions": {
78
54
  "Step": {
79
55
  "type": "object",
80
- "required": ["id", "type"],
56
+ "required": ["step_id", "type"],
81
57
  "oneOf": [
82
58
  { "$ref": "#/definitions/TaskStep" },
83
59
  { "$ref": "#/definitions/ForeachStep" },
@@ -90,12 +66,14 @@
90
66
  },
91
67
  "TaskStep": {
92
68
  "type": "object",
93
- "required": ["id", "type", "service"],
69
+ "required": ["step_id", "type", "service"],
94
70
  "additionalProperties": true,
95
71
  "properties": {
96
- "id": {
72
+ "step_id": {
97
73
  "type": "string",
98
- "minLength": 1
74
+ "minLength": 1,
75
+ "pattern": "^[a-zA-Z0-9_]+$",
76
+ "description": "V2: step_id (snake_case, must match object key)"
99
77
  },
100
78
  "type": {
101
79
  "type": "string",
@@ -109,14 +87,6 @@
109
87
  "type": "string",
110
88
  "minLength": 1
111
89
  },
112
- "action": {
113
- "type": "string",
114
- "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"]
115
- },
116
- "endpoint": {
117
- "type": "string",
118
- "minLength": 1
119
- },
120
90
  "operation": {
121
91
  "type": "string",
122
92
  "minLength": 1,
@@ -124,20 +94,15 @@
124
94
  },
125
95
  "input": {
126
96
  "type": "object",
127
- "additionalProperties": true,
128
- "description": "Optional input mapping - supports all JSON types"
97
+ "additionalProperties": true
129
98
  },
130
99
  "output": {
131
100
  "type": "object",
132
- "additionalProperties": true,
133
- "description": "Optional output mapping - supports all JSON types"
101
+ "additionalProperties": true
134
102
  },
135
103
  "depends_on": {
136
104
  "type": "array",
137
- "items": {
138
- "type": "string",
139
- "minLength": 1
140
- }
105
+ "items": { "type": "string" }
141
106
  },
142
107
  "retry": {
143
108
  "$ref": "#/definitions/Retry"
@@ -150,130 +115,91 @@
150
115
  "$ref": "#/definitions/ErrorHandler"
151
116
  },
152
117
  "condition": {
153
- "type": "string",
154
- "description": "JSONPath expression that must evaluate to true for step to execute"
118
+ "type": "string"
119
+ },
120
+ "expect": {
121
+ "type": "object",
122
+ "description": "Expected output for testing"
155
123
  }
156
- },
157
- "condition": {
158
- "type": "string",
159
- "description": "JSONPath expression for conditional execution"
160
- },
161
- "timeoutMs": {
162
- "type": "integer",
163
- "minimum": 1,
164
- "description": "Timeout for entire foreach loop"
165
- },
166
- "on_error": {
167
- "$ref": "#/definitions/ErrorHandler"
168
124
  }
169
125
  },
170
126
  "ForeachStep": {
171
127
  "type": "object",
172
- "required": ["id", "type", "iterator", "body"],
128
+ "required": ["step_id", "type", "iterator", "body"],
173
129
  "additionalProperties": true,
174
130
  "properties": {
175
- "id": {
131
+ "step_id": {
176
132
  "type": "string",
177
- "minLength": 1
133
+ "minLength": 1,
134
+ "pattern": "^[a-zA-Z0-9_]+$"
178
135
  },
179
136
  "type": {
180
137
  "type": "string",
181
138
  "const": "foreach"
182
139
  },
183
- "name": {
184
- "type": "string",
185
- "minLength": 1
186
- },
187
140
  "iterator": {
188
141
  "type": "string",
189
142
  "minLength": 1
190
143
  },
191
144
  "body": {
192
- "type": "array",
193
- "minItems": 1,
194
- "items": {
145
+ "type": "object",
146
+ "minProperties": 1,
147
+ "additionalProperties": {
195
148
  "$ref": "#/definitions/Step"
196
149
  }
197
- },
198
- "output": {
199
- "type": "object",
200
- "additionalProperties": true,
201
- "description": "Optional output collection mapping"
202
150
  }
203
151
  }
204
152
  },
205
153
  "ForkJoinStep": {
206
154
  "type": "object",
207
- "required": ["id", "type", "branches", "join"],
155
+ "required": ["step_id", "type", "branches", "join"],
208
156
  "additionalProperties": true,
209
157
  "properties": {
210
- "id": {
158
+ "step_id": {
211
159
  "type": "string",
212
- "minLength": 1
160
+ "minLength": 1,
161
+ "pattern": "^[a-zA-Z0-9_]+$"
213
162
  },
214
163
  "type": {
215
164
  "type": "string",
216
165
  "const": "fork_join"
217
166
  },
218
- "name": {
219
- "type": "string",
220
- "minLength": 1
221
- },
222
167
  "branches": {
223
- "type": "array",
224
- "minItems": 1,
225
- "items": {
168
+ "type": "object",
169
+ "minProperties": 1,
170
+ "additionalProperties": {
226
171
  "$ref": "#/definitions/Step"
227
172
  }
228
173
  },
229
174
  "join": {
230
175
  "type": "object",
231
176
  "required": ["strategy"],
232
- "additionalProperties": false,
233
177
  "properties": {
234
178
  "strategy": {
235
179
  "type": "string",
236
- "enum": ["merge", "first", "last", "all", "custom"],
237
- "description": "How to combine branch results"
180
+ "enum": ["merge", "first", "last", "all", "custom"]
238
181
  },
239
182
  "output": {
240
- "type": "object",
241
- "additionalProperties": true,
242
- "description": "Optional output mapping for joined results"
183
+ "type": "object"
243
184
  }
244
185
  }
245
186
  }
246
- },
247
- "condition": {
248
- "type": "string",
249
- "description": "JSONPath expression for conditional execution"
250
- },
251
- "timeoutMs": {
252
- "type": "integer",
253
- "minimum": 1,
254
- "description": "Timeout for all branches"
255
- },
256
- "on_error": {
257
- "$ref": "#/definitions/ErrorHandler"
258
187
  }
259
188
  },
260
189
  "SwitchStep": {
261
190
  "type": "object",
262
- "required": ["id", "type", "expression", "cases", "default"],
191
+ "required": ["step_id", "type", "expression", "cases"],
263
192
  "additionalProperties": true,
264
193
  "properties": {
265
- "id": {
194
+ "step_id": {
266
195
  "type": "string",
267
- "minLength": 1
196
+ "minLength": 1,
197
+ "pattern": "^[a-zA-Z0-9_]+$"
268
198
  },
269
199
  "type": {
270
200
  "type": "string",
271
201
  "const": "switch"
272
202
  },
273
- "name": {
274
- "type": "string",
275
- "minLength": 1
276
- },
277
203
  "expression": {
278
204
  "type": "string",
279
205
  "minLength": 1
@@ -287,138 +213,82 @@
287
213
  },
288
214
  "default": {
289
215
  "$ref": "#/definitions/Step"
290
- },
291
- "depends_on": {
292
- "type": "array",
293
- "items": {
294
- "type": "string",
295
- "minLength": 1
296
- }
297
- },
298
- "condition": {
299
- "type": "string",
300
- "description": "JSONPath expression for conditional execution"
301
- },
302
- "on_error": {
303
- "$ref": "#/definitions/ErrorHandler"
304
216
  }
305
217
  }
306
218
  },
307
219
  "SubWorkflowStep": {
308
220
  "type": "object",
309
- "required": ["id", "type", "steps"],
221
+ "required": ["step_id", "type", "steps"],
310
222
  "additionalProperties": true,
311
223
  "properties": {
312
- "id": {
224
+ "step_id": {
313
225
  "type": "string",
314
- "minLength": 1
226
+ "minLength": 1,
227
+ "pattern": "^[a-zA-Z0-9_]+$"
315
228
  },
316
229
  "type": {
317
230
  "type": "string",
318
231
  "const": "sub_workflow"
319
232
  },
320
- "name": {
321
- "type": "string",
322
- "minLength": 1
323
- },
324
233
  "input": {
325
- "type": "object",
326
- "additionalProperties": true,
327
- "description": "Optional input for sub-workflow - supports all JSON types"
234
+ "type": "object"
328
235
  },
329
236
  "steps": {
330
- "type": "array",
331
- "minItems": 1,
332
- "items": {
237
+ "type": "object",
238
+ "minProperties": 1,
239
+ "additionalProperties": {
333
240
  "$ref": "#/definitions/Step"
334
241
  }
335
- },
336
- "condition": {
337
- "type": "string",
338
- "description": "JSONPath expression for conditional execution"
339
- },
340
- "timeoutMs": {
341
- "type": "integer",
342
- "minimum": 1,
343
- "description": "Timeout for entire sub-workflow"
344
- },
345
- "on_error": {
346
- "$ref": "#/definitions/ErrorHandler"
347
242
  }
348
243
  }
349
244
  },
350
245
  "WaitStep": {
351
246
  "type": "object",
352
- "required": ["id", "type", "durationMs"],
247
+ "required": ["step_id", "type", "durationMs"],
353
248
  "additionalProperties": true,
354
249
  "properties": {
355
- "id": {
250
+ "step_id": {
356
251
  "type": "string",
357
- "minLength": 1
252
+ "minLength": 1,
253
+ "pattern": "^[a-zA-Z0-9_]+$"
358
254
  },
359
255
  "type": {
360
256
  "type": "string",
361
257
  "const": "wait"
362
258
  },
363
- "name": {
364
- "type": "string",
365
- "minLength": 1
366
- },
367
259
  "durationMs": {
368
260
  "type": "integer",
369
261
  "minimum": 0
370
- },
371
- "condition": {
372
- "type": "string",
373
- "description": "JSONPath expression for conditional execution"
374
262
  }
375
263
  }
376
264
  },
377
265
  "DispatchStep": {
378
266
  "type": "object",
379
- "required": ["id", "type", "method", "target"],
267
+ "required": ["step_id", "type", "method", "target"],
380
268
  "additionalProperties": true,
381
269
  "properties": {
382
- "id": {
270
+ "step_id": {
383
271
  "type": "string",
384
- "minLength": 1
272
+ "minLength": 1,
273
+ "pattern": "^[a-zA-Z0-9_]+$"
385
274
  },
386
275
  "type": {
387
276
  "type": "string",
388
277
  "const": "dispatch"
389
278
  },
390
- "name": {
391
- "type": "string",
392
- "minLength": 1
393
- },
394
279
  "method": {
395
280
  "type": "string",
396
- "enum": ["webhook", "http", "grpc", "kafka", "sqs", "custom"],
397
- "description": "Dispatch method to use"
281
+ "enum": ["webhook", "http", "grpc", "kafka", "sqs", "custom"]
398
282
  },
399
283
  "target": {
400
284
  "type": "string",
401
285
  "format": "uri"
402
286
  },
403
287
  "input": {
404
- "type": "object",
405
- "additionalProperties": true,
406
- "description": "Optional dispatch payload - supports all JSON types"
288
+ "type": "object"
407
289
  },
408
290
  "retry": {
409
291
  "$ref": "#/definitions/Retry"
410
- },
411
- "condition": {
412
- "type": "string",
413
- "description": "JSONPath expression for conditional execution"
414
- },
415
- "timeoutMs": {
416
- "type": "integer",
417
- "minimum": 1,
418
- "description": "Timeout for dispatch operation"
419
- },
420
- "on_error": {
421
- "$ref": "#/definitions/ErrorHandler"
422
292
  }
423
293
  }
424
294
  },
@@ -438,45 +308,38 @@
438
308
  "backoffMultiplier": {
439
309
  "type": "number",
440
310
  "minimum": 1.0,
441
- "maximum": 5.0,
442
- "description": "Exponential backoff multiplier"
311
+ "maximum": 5.0
443
312
  }
444
313
  }
445
314
  },
446
315
  "ErrorHandler": {
447
316
  "type": "object",
317
+ "required": ["strategy"],
448
318
  "additionalProperties": false,
449
319
  "properties": {
450
320
  "strategy": {
451
321
  "type": "string",
452
- "enum": ["retry", "fail", "continue", "compensate", "fallback"],
453
- "description": "How to handle errors"
322
+ "enum": ["retry", "fail", "continue", "compensate", "fallback"]
454
323
  },
455
324
  "retries": {
456
325
  "type": "integer",
457
326
  "minimum": 0,
458
- "maximum": 10,
459
- "description": "Number of retries before applying strategy"
327
+ "maximum": 10
460
328
  },
461
329
  "retryDelayMs": {
462
330
  "type": "integer",
463
- "minimum": 0,
464
- "description": "Delay between retries"
331
+ "minimum": 0
465
332
  },
466
333
  "fallbackStep": {
467
- "type": "string",
468
- "description": "Step ID to execute as fallback"
334
+ "type": "string"
469
335
  },
470
336
  "compensationStep": {
471
- "type": "string",
472
- "description": "Step ID to execute for compensation/rollback"
337
+ "type": "string"
473
338
  },
474
339
  "errorOutput": {
475
- "type": "string",
476
- "description": "JSONPath to store error details"
340
+ "type": "string"
477
341
  }
478
- },
479
- "required": ["strategy"]
342
+ }
480
343
  }
481
344
  }
482
- }
345
+ }
@@ -29,19 +29,18 @@ class CookbookValidationError extends Error {
29
29
  }
30
30
 
31
31
  /**
32
- * Load schema file with version detection
32
+ * Load V2 schema - V1 IS BANNED, only one schema exists
33
33
  */
34
34
  function loadSchema(version = '2.0') {
35
- const schemaMap = {
36
- '1.0': 'cookbook.schema.json', // Legacy v1.0 schema
37
- '2.0': 'cookbook.v2.schema.json' // New v2.0 schema with snake_case
38
- };
35
+ // V1 is BANNED - reject immediately
36
+ if (version.startsWith('1.')) {
37
+ throw new CookbookValidationError('V1 FORMAT BANNED! Use version 2.x.x');
38
+ }
39
39
 
40
- const filename = schemaMap[version.split('.')[0] + '.0'] || schemaMap['2.0'];
41
- const schemaPath = path.join(__dirname, '../../schemas', filename);
40
+ const schemaPath = path.join(__dirname, '../../schemas/cookbook.schema.json');
42
41
 
43
42
  if (!fs.existsSync(schemaPath)) {
44
- throw new CookbookValidationError(`Schema file not found: ${filename}`);
43
+ throw new CookbookValidationError('Schema file not found: cookbook.schema.json');
45
44
  }
46
45
 
47
46
  return JSON.parse(fs.readFileSync(schemaPath, 'utf-8'));
@@ -249,53 +248,48 @@ function validateSemantics(cookbook, version) {
249
248
  stepIds.add(stepId);
250
249
  }
251
250
 
252
- // Check nested steps
253
- if (step.steps) {
254
- step.steps.forEach((nested, idx) =>
255
- checkStep(nested, `${path}.steps[${idx}]`)
251
+ // Check nested steps - V2: all nested steps are objects keyed by step_id
252
+ if (step.steps && typeof step.steps === 'object') {
253
+ Object.entries(step.steps).forEach(([nestedKey, nested]) =>
254
+ checkStep(nested, `${path}.steps.${nestedKey}`)
256
255
  );
257
256
  }
258
- if (step.body) {
259
- step.body.forEach((nested, idx) =>
260
- checkStep(nested, `${path}.body[${idx}]`)
257
+ if (step.body && typeof step.body === 'object') {
258
+ Object.entries(step.body).forEach(([nestedKey, nested]) =>
259
+ checkStep(nested, `${path}.body.${nestedKey}`)
261
260
  );
262
261
  }
263
- if (step.branches) {
264
- step.branches.forEach((branch, idx) => {
265
- if (branch.steps) {
266
- branch.steps.forEach((nested, jdx) =>
267
- checkStep(nested, `${path}.branches[${idx}].steps[${jdx}]`)
268
- );
269
- }
262
+ if (step.branches && typeof step.branches === 'object') {
263
+ Object.entries(step.branches).forEach(([branchKey, branch]) => {
264
+ checkStep(branch, `${path}.branches.${branchKey}`);
270
265
  });
271
266
  }
272
267
  if (step.cases) {
273
268
  Object.keys(step.cases).forEach(caseKey => {
274
269
  const caseVal = step.cases[caseKey];
275
- if (caseVal.steps) {
276
- caseVal.steps.forEach((nested, idx) =>
277
- checkStep(nested, `${path}.cases.${caseKey}.steps[${idx}]`)
278
- );
279
- }
270
+ checkStep(caseVal, `${path}.cases.${caseKey}`);
280
271
  });
281
272
  }
282
273
  }
283
274
 
284
- // Check all steps
275
+ // Check all steps - V2: steps is an OBJECT keyed by step_id, NOT an array
285
276
  if (cookbook.steps) {
286
- cookbook.steps.forEach((step, idx) =>
287
- checkStep(step, `steps[${idx}]`)
277
+ if (Array.isArray(cookbook.steps)) {
278
+ throw new CookbookValidationError('V1 FORMAT BANNED! steps must be an object keyed by step_id, not an array');
279
+ }
280
+ Object.entries(cookbook.steps).forEach(([stepKey, step]) =>
281
+ checkStep(step, `steps.${stepKey}`)
288
282
  );
289
283
  }
290
284
 
291
285
  // Check dependencies exist
292
286
  if (version === '2.0' && cookbook.steps) {
293
- cookbook.steps.forEach((step, idx) => {
287
+ Object.entries(cookbook.steps).forEach(([stepKey, step]) => {
294
288
  if (step.depends_on) {
295
289
  step.depends_on.forEach(dep => {
296
290
  if (!stepIds.has(dep)) {
297
291
  throw new CookbookValidationError(
298
- `Step ${idx} depends on non-existent step_id '${dep}'`
292
+ `Step '${stepKey}' depends on non-existent step_id '${dep}'`
299
293
  );
300
294
  }
301
295
  });
@@ -1,728 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "https://onlineapps.com/schemas/cookbook/v2.1.0/cookbook.schema.json",
4
- "title": "Cookbook Workflow Schema v2.1",
5
- "description": "Schema for defining workflow orchestration with snake_case convention",
6
- "type": "object",
7
- "required": ["version", "steps"],
8
- "additionalProperties": false,
9
- "properties": {
10
- "version": {
11
- "type": "string",
12
- "pattern": "^2\\.\\d+\\.\\d+$",
13
- "description": "Schema version (must start with 2.x.x)"
14
- },
15
- "name": {
16
- "type": "string",
17
- "description": "Human-readable workflow name"
18
- },
19
- "description": {
20
- "type": "string",
21
- "description": "Workflow description"
22
- },
23
- "config": {
24
- "$ref": "#/definitions/Config",
25
- "description": "Global configuration and defaults"
26
- },
27
- "api_input": {
28
- "type": "object",
29
- "description": "Expected input parameters for the workflow",
30
- "additionalProperties": true
31
- },
32
- "steps": {
33
- "type": "array",
34
- "minItems": 1,
35
- "items": {
36
- "$ref": "#/definitions/Step"
37
- },
38
- "description": "Array of workflow steps"
39
- },
40
- "delivery": {
41
- "$ref": "#/definitions/DeliveryConfig",
42
- "description": "Delivery strategy and destinations for workflow output"
43
- }
44
- },
45
- "definitions": {
46
- "Config": {
47
- "type": "object",
48
- "additionalProperties": false,
49
- "properties": {
50
- "default_timeout_ms": {
51
- "type": "integer",
52
- "minimum": 1,
53
- "description": "Default timeout for all steps in milliseconds"
54
- },
55
- "default_retry": {
56
- "$ref": "#/definitions/RetryConfig",
57
- "description": "Default retry configuration for all steps"
58
- }
59
- }
60
- },
61
- "Step": {
62
- "type": "object",
63
- "required": ["step_id", "type"],
64
- "oneOf": [
65
- { "$ref": "#/definitions/TaskStep" },
66
- { "$ref": "#/definitions/ForeachStep" },
67
- { "$ref": "#/definitions/ForkJoinStep" },
68
- { "$ref": "#/definitions/SwitchStep" },
69
- { "$ref": "#/definitions/SubWorkflowStep" },
70
- { "$ref": "#/definitions/WaitStep" },
71
- { "$ref": "#/definitions/DispatchStep" }
72
- ]
73
- },
74
- "TaskStep": {
75
- "type": "object",
76
- "required": ["step_id", "type", "service", "operation"],
77
- "additionalProperties": false,
78
- "properties": {
79
- "step_id": {
80
- "type": "string",
81
- "pattern": "^[a-zA-Z0-9_]+$",
82
- "description": "Unique identifier for the step"
83
- },
84
- "type": {
85
- "const": "task",
86
- "description": "Step type"
87
- },
88
- "name": {
89
- "type": "string",
90
- "description": "Human-readable step name"
91
- },
92
- "service": {
93
- "type": "string",
94
- "pattern": "^[a-z0-9-]+$",
95
- "description": "Target microservice name"
96
- },
97
- "operation": {
98
- "type": "string",
99
- "description": "Operation ID from OpenAPI spec (required for task steps)"
100
- },
101
- "input": {
102
- "type": "object",
103
- "additionalProperties": true,
104
- "description": "Input parameters for the operation"
105
- },
106
- "output": {
107
- "type": "object",
108
- "additionalProperties": true,
109
- "description": "Output mapping to workflow context"
110
- },
111
- "timeout_ms": {
112
- "type": "integer",
113
- "minimum": 1,
114
- "description": "Timeout in milliseconds (overrides default)"
115
- },
116
- "retry": {
117
- "$ref": "#/definitions/RetryConfig",
118
- "description": "Retry configuration (overrides default)"
119
- },
120
- "on_error": {
121
- "$ref": "#/definitions/ErrorHandler",
122
- "description": "Error handling configuration"
123
- },
124
- "compensate": {
125
- "$ref": "#/definitions/CompensationStep",
126
- "description": "Rollback/compensation configuration"
127
- },
128
- "depends_on": {
129
- "type": "array",
130
- "items": {
131
- "type": "string"
132
- },
133
- "description": "Array of step_ids this step depends on"
134
- }
135
- }
136
- },
137
- "ForeachStep": {
138
- "type": "object",
139
- "required": ["step_id", "type", "iterator", "steps"],
140
- "additionalProperties": false,
141
- "properties": {
142
- "step_id": {
143
- "type": "string",
144
- "pattern": "^[a-zA-Z0-9_]+$"
145
- },
146
- "type": {
147
- "const": "foreach"
148
- },
149
- "name": {
150
- "type": "string"
151
- },
152
- "iterator": {
153
- "type": "string",
154
- "pattern": "^\\$\\{.+\\}$",
155
- "description": "Reference to array to iterate over"
156
- },
157
- "steps": {
158
- "type": "array",
159
- "minItems": 1,
160
- "items": {
161
- "$ref": "#/definitions/Step"
162
- },
163
- "description": "Steps to execute for each item"
164
- },
165
- "output": {
166
- "type": "object",
167
- "additionalProperties": true,
168
- "description": "How to aggregate results"
169
- },
170
- "max_concurrency": {
171
- "type": "integer",
172
- "minimum": 1,
173
- "description": "Maximum parallel executions"
174
- },
175
- "on_error": {
176
- "$ref": "#/definitions/ErrorHandler"
177
- },
178
- "depends_on": {
179
- "type": "array",
180
- "items": {
181
- "type": "string"
182
- }
183
- }
184
- }
185
- },
186
- "ForkJoinStep": {
187
- "type": "object",
188
- "required": ["step_id", "type", "branches", "join"],
189
- "additionalProperties": false,
190
- "properties": {
191
- "step_id": {
192
- "type": "string",
193
- "pattern": "^[a-zA-Z0-9_]+$"
194
- },
195
- "type": {
196
- "const": "fork_join"
197
- },
198
- "name": {
199
- "type": "string"
200
- },
201
- "branches": {
202
- "type": "array",
203
- "minItems": 2,
204
- "items": {
205
- "type": "object",
206
- "required": ["branch_id", "steps"],
207
- "properties": {
208
- "branch_id": {
209
- "type": "string"
210
- },
211
- "steps": {
212
- "type": "array",
213
- "minItems": 1,
214
- "items": {
215
- "$ref": "#/definitions/Step"
216
- }
217
- }
218
- }
219
- },
220
- "description": "Parallel branches to execute"
221
- },
222
- "join": {
223
- "type": "object",
224
- "required": ["strategy"],
225
- "properties": {
226
- "strategy": {
227
- "enum": ["all", "first", "race", "merge"],
228
- "description": "How to join branch results"
229
- },
230
- "timeout_ms": {
231
- "type": "integer",
232
- "minimum": 1
233
- }
234
- }
235
- },
236
- "output": {
237
- "type": "object",
238
- "additionalProperties": true
239
- },
240
- "on_error": {
241
- "$ref": "#/definitions/ErrorHandler"
242
- },
243
- "depends_on": {
244
- "type": "array",
245
- "items": {
246
- "type": "string"
247
- }
248
- }
249
- }
250
- },
251
- "SwitchStep": {
252
- "type": "object",
253
- "required": ["step_id", "type", "expression", "cases", "default"],
254
- "additionalProperties": false,
255
- "properties": {
256
- "step_id": {
257
- "type": "string",
258
- "pattern": "^[a-zA-Z0-9_]+$"
259
- },
260
- "type": {
261
- "const": "switch"
262
- },
263
- "name": {
264
- "type": "string"
265
- },
266
- "expression": {
267
- "type": "string",
268
- "pattern": "^\\$\\{.+\\}$",
269
- "description": "Expression to evaluate for switch"
270
- },
271
- "cases": {
272
- "type": "object",
273
- "additionalProperties": {
274
- "type": "object",
275
- "required": ["steps"],
276
- "properties": {
277
- "steps": {
278
- "type": "array",
279
- "minItems": 1,
280
- "items": {
281
- "$ref": "#/definitions/Step"
282
- }
283
- }
284
- }
285
- },
286
- "description": "Case branches"
287
- },
288
- "default": {
289
- "type": "object",
290
- "required": ["steps"],
291
- "properties": {
292
- "steps": {
293
- "type": "array",
294
- "minItems": 1,
295
- "items": {
296
- "$ref": "#/definitions/Step"
297
- }
298
- }
299
- },
300
- "description": "Default branch (required)"
301
- },
302
- "on_error": {
303
- "$ref": "#/definitions/ErrorHandler"
304
- },
305
- "depends_on": {
306
- "type": "array",
307
- "items": {
308
- "type": "string"
309
- }
310
- }
311
- }
312
- },
313
- "SubWorkflowStep": {
314
- "type": "object",
315
- "required": ["step_id", "type", "workflow_id"],
316
- "additionalProperties": false,
317
- "properties": {
318
- "step_id": {
319
- "type": "string",
320
- "pattern": "^[a-zA-Z0-9_]+$"
321
- },
322
- "type": {
323
- "const": "sub_workflow"
324
- },
325
- "name": {
326
- "type": "string"
327
- },
328
- "workflow_id": {
329
- "type": "string",
330
- "description": "ID of workflow to execute"
331
- },
332
- "parameters": {
333
- "type": "object",
334
- "additionalProperties": true,
335
- "description": "Parameters to pass to sub-workflow"
336
- },
337
- "output": {
338
- "type": "object",
339
- "additionalProperties": true
340
- },
341
- "timeout_ms": {
342
- "type": "integer",
343
- "minimum": 1
344
- },
345
- "on_error": {
346
- "$ref": "#/definitions/ErrorHandler"
347
- },
348
- "depends_on": {
349
- "type": "array",
350
- "items": {
351
- "type": "string"
352
- }
353
- }
354
- }
355
- },
356
- "WaitStep": {
357
- "type": "object",
358
- "required": ["step_id", "type", "duration_ms"],
359
- "additionalProperties": false,
360
- "properties": {
361
- "step_id": {
362
- "type": "string",
363
- "pattern": "^[a-zA-Z0-9_]+$"
364
- },
365
- "type": {
366
- "const": "wait"
367
- },
368
- "name": {
369
- "type": "string"
370
- },
371
- "duration_ms": {
372
- "type": "integer",
373
- "minimum": 1,
374
- "description": "Wait duration in milliseconds"
375
- },
376
- "message": {
377
- "type": "string",
378
- "description": "Optional message explaining the wait"
379
- },
380
- "depends_on": {
381
- "type": "array",
382
- "items": {
383
- "type": "string"
384
- }
385
- }
386
- }
387
- },
388
- "DispatchStep": {
389
- "type": "object",
390
- "required": ["step_id", "type", "method", "target"],
391
- "additionalProperties": false,
392
- "properties": {
393
- "step_id": {
394
- "type": "string",
395
- "pattern": "^[a-zA-Z0-9_]+$"
396
- },
397
- "type": {
398
- "const": "dispatch"
399
- },
400
- "name": {
401
- "type": "string"
402
- },
403
- "method": {
404
- "enum": ["webhook", "http", "grpc", "kafka", "sqs"],
405
- "description": "Dispatch method"
406
- },
407
- "target": {
408
- "type": "string",
409
- "description": "Target URL or address"
410
- },
411
- "input": {
412
- "type": "object",
413
- "additionalProperties": true
414
- },
415
- "headers": {
416
- "type": "object",
417
- "additionalProperties": {
418
- "type": "string"
419
- }
420
- },
421
- "timeout_ms": {
422
- "type": "integer",
423
- "minimum": 1
424
- },
425
- "retry": {
426
- "$ref": "#/definitions/RetryConfig"
427
- },
428
- "on_error": {
429
- "$ref": "#/definitions/ErrorHandler"
430
- },
431
- "depends_on": {
432
- "type": "array",
433
- "items": {
434
- "type": "string"
435
- }
436
- }
437
- }
438
- },
439
- "RetryConfig": {
440
- "type": "object",
441
- "additionalProperties": false,
442
- "properties": {
443
- "max_attempts": {
444
- "type": "integer",
445
- "minimum": 1,
446
- "description": "Maximum retry attempts"
447
- },
448
- "delay_ms": {
449
- "type": "integer",
450
- "minimum": 0,
451
- "description": "Delay between retries in milliseconds"
452
- },
453
- "backoff": {
454
- "enum": ["linear", "exponential"],
455
- "description": "Backoff strategy"
456
- },
457
- "max_delay_ms": {
458
- "type": "integer",
459
- "minimum": 1,
460
- "description": "Maximum delay for exponential backoff"
461
- }
462
- }
463
- },
464
- "ErrorHandler": {
465
- "type": "object",
466
- "additionalProperties": false,
467
- "properties": {
468
- "strategy": {
469
- "enum": ["retry", "fail", "continue", "compensate"],
470
- "description": "Error handling strategy"
471
- },
472
- "retry": {
473
- "$ref": "#/definitions/RetryConfig"
474
- },
475
- "catch": {
476
- "type": "array",
477
- "items": {
478
- "type": "object",
479
- "required": ["error_type", "handler_step"],
480
- "properties": {
481
- "error_type": {
482
- "type": "string",
483
- "description": "Type of error to catch"
484
- },
485
- "handler_step": {
486
- "type": "string",
487
- "description": "Step to execute on this error"
488
- }
489
- }
490
- },
491
- "description": "Specific error handlers"
492
- },
493
- "finally": {
494
- "type": "string",
495
- "description": "Step to always execute"
496
- }
497
- }
498
- },
499
- "CompensationStep": {
500
- "type": "object",
501
- "required": ["step_id", "service", "operation"],
502
- "additionalProperties": false,
503
- "properties": {
504
- "step_id": {
505
- "type": "string",
506
- "pattern": "^[a-zA-Z0-9_]+$",
507
- "description": "ID for compensation step"
508
- },
509
- "service": {
510
- "type": "string",
511
- "pattern": "^[a-z0-9-]+$"
512
- },
513
- "operation": {
514
- "type": "string"
515
- },
516
- "input": {
517
- "type": "object",
518
- "additionalProperties": true
519
- }
520
- }
521
- },
522
- "DeliveryConfig": {
523
- "type": "object",
524
- "required": ["handler"],
525
- "additionalProperties": false,
526
- "properties": {
527
- "handler": {
528
- "enum": ["dispatcher", "service_step", "none"],
529
- "description": "Delivery handler selection"
530
- },
531
- "allow_skip": {
532
- "type": "boolean",
533
- "default": false,
534
- "description": "Allow dispatcher to skip delivery without failing workflow"
535
- },
536
- "delivery_step": {
537
- "type": "string",
538
- "pattern": "^[a-zA-Z0-9_]+$",
539
- "description": "Step ID that performs delivery when handler=service_step"
540
- },
541
- "destinations": {
542
- "type": "array",
543
- "items": {
544
- "$ref": "#/definitions/DeliveryDestination"
545
- },
546
- "default": []
547
- },
548
- "output": {
549
- "$ref": "#/definitions/DeliveryOutput"
550
- }
551
- },
552
- "allOf": [
553
- {
554
- "if": {
555
- "properties": {
556
- "handler": { "const": "dispatcher" }
557
- }
558
- },
559
- "then": {
560
- "required": ["destinations", "output"],
561
- "properties": {
562
- "destinations": {
563
- "minItems": 1
564
- }
565
- }
566
- }
567
- },
568
- {
569
- "if": {
570
- "properties": {
571
- "handler": { "const": "service_step" }
572
- }
573
- },
574
- "then": {
575
- "required": ["delivery_step", "output"]
576
- }
577
- }
578
- ]
579
- },
580
- "DeliveryDestination": {
581
- "type": "object",
582
- "required": ["type"],
583
- "properties": {
584
- "type": {
585
- "enum": ["webhook", "websocket", "public_url"],
586
- "description": "Delivery channel type"
587
- },
588
- "name": {
589
- "type": "string",
590
- "pattern": "^[a-zA-Z0-9._-]+$",
591
- "description": "Optional identifier for monitoring"
592
- },
593
- "enabled": {
594
- "type": "boolean",
595
- "default": true
596
- },
597
- "retry": {
598
- "$ref": "#/definitions/RetryConfig"
599
- }
600
- },
601
- "allOf": [
602
- {
603
- "if": {
604
- "properties": {
605
- "type": { "const": "webhook" }
606
- }
607
- },
608
- "then": {
609
- "$ref": "#/definitions/DeliveryWebhookOptions"
610
- }
611
- },
612
- {
613
- "if": {
614
- "properties": {
615
- "type": { "const": "websocket" }
616
- }
617
- },
618
- "then": {
619
- "$ref": "#/definitions/DeliveryWebsocketOptions"
620
- }
621
- },
622
- {
623
- "if": {
624
- "properties": {
625
- "type": { "const": "public_url" }
626
- }
627
- },
628
- "then": {
629
- "$ref": "#/definitions/DeliveryPublicUrlOptions"
630
- }
631
- }
632
- ]
633
- },
634
- "DeliveryWebhookOptions": {
635
- "type": "object",
636
- "required": ["url", "method"],
637
- "properties": {
638
- "url": {
639
- "type": "string",
640
- "format": "uri",
641
- "description": "Target webhook URL"
642
- },
643
- "method": {
644
- "type": "string",
645
- "enum": ["GET", "POST", "PUT", "PATCH", "DELETE"]
646
- },
647
- "headers": {
648
- "type": "object",
649
- "additionalProperties": {
650
- "type": "string"
651
- }
652
- },
653
- "body_template": {
654
- "type": ["object", "array"],
655
- "description": "Optional custom payload template"
656
- },
657
- "timeout_ms": {
658
- "type": "integer",
659
- "minimum": 100,
660
- "description": "Request timeout"
661
- }
662
- }
663
- },
664
- "DeliveryWebsocketOptions": {
665
- "type": "object",
666
- "properties": {
667
- "client_id": {
668
- "type": "string",
669
- "description": "Client session identifier"
670
- },
671
- "tenant_id": {
672
- "type": "string",
673
- "description": "Tenant/topic identifier"
674
- },
675
- "events": {
676
- "type": "array",
677
- "items": {
678
- "type": "string"
679
- }
680
- }
681
- },
682
- "anyOf": [
683
- { "required": ["client_id"] },
684
- { "required": ["tenant_id"] }
685
- ]
686
- },
687
- "DeliveryPublicUrlOptions": {
688
- "type": "object",
689
- "required": ["path"],
690
- "properties": {
691
- "path": {
692
- "type": "string",
693
- "pattern": "^[a-zA-Z0-9._\\-/\\${}]+$",
694
- "description": "Storage key / relative path"
695
- },
696
- "ttl_seconds": {
697
- "type": "integer",
698
- "minimum": 60,
699
- "description": "Time to live for generated URL"
700
- },
701
- "access": {
702
- "type": "string",
703
- "enum": ["public", "signed"],
704
- "default": "signed"
705
- },
706
- "max_downloads": {
707
- "type": "integer",
708
- "minimum": 1
709
- }
710
- }
711
- },
712
- "DeliveryOutput": {
713
- "type": "object",
714
- "propertyNames": {
715
- "pattern": "^[a-zA-Z0-9_]+$"
716
- },
717
- "additionalProperties": {
718
- "type": [
719
- "string",
720
- "number",
721
- "boolean",
722
- "object",
723
- "array"
724
- ]
725
- }
726
- }
727
- }
728
- }