@onlineapps/cookbook-core 2.1.15 → 2.1.16

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.15",
3
+ "version": "2.1.16",
4
4
  "description": "Core cookbook parsing and validation - lightweight foundation for workflow definitions",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -1,351 +1,134 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "https://onlineapps.cz/schemas/cookbook-relaxed/v2.0.0",
4
- "title": "Cookbook Schema - Relaxed Mode",
5
- "description": "Relaxed validation schema for development and migration with flexible requirements",
3
+ "$id": "https://onlineapps.cz/schemas/cookbook-relaxed/v2.1.0",
4
+ "title": "Cookbook Schema - Relaxed Mode (V2.1)",
5
+ "description": "Relaxed validation schema for V2.1 cookbooks (allows additional properties on root).",
6
6
  "type": "object",
7
- "required": ["steps"],
7
+ "required": ["steps", "version"],
8
8
  "additionalProperties": true,
9
9
  "properties": {
10
- "name": {
11
- "type": "string"
12
- },
13
- "version": {
14
- "type": "string",
15
- "description": "Recommended: Use semantic versioning (e.g., 1.0.0)"
16
- },
17
- "description": {
18
- "type": "string"
19
- },
20
- "api_input": {
21
- "type": "object"
22
- },
10
+ "name": { "type": "string" },
11
+ "version": { "type": "string", "pattern": "^2\\.\\d+\\.\\d+$" },
12
+ "description": { "type": "string" },
13
+ "api_input": { "type": "object" },
14
+ "metadata": { "type": "object", "additionalProperties": true },
23
15
  "steps": {
24
16
  "type": "array",
25
17
  "minItems": 1,
26
- "items": {
27
- "$ref": "#/definitions/Step"
28
- }
29
- }
18
+ "items": { "$ref": "#/definitions/Step" }
19
+ },
20
+ "delivery": { "type": "object" }
30
21
  },
31
22
  "definitions": {
32
23
  "Step": {
33
24
  "type": "object",
34
- "required": ["id"],
25
+ "required": ["step_id", "type"],
35
26
  "additionalProperties": true,
36
- "properties": {
37
- "id": {
38
- "type": "string",
39
- "minLength": 1
40
- },
41
- "type": {
42
- "type": "string",
43
- "enum": ["task", "foreach", "fork_join", "switch", "sub_workflow", "wait", "dispatch"],
44
- "description": "Recommended: Explicitly specify step type"
45
- }
46
- },
47
27
  "oneOf": [
48
28
  { "$ref": "#/definitions/TaskStep" },
49
29
  { "$ref": "#/definitions/ForeachStep" },
50
30
  { "$ref": "#/definitions/ForkJoinStep" },
51
31
  { "$ref": "#/definitions/SwitchStep" },
52
- { "$ref": "#/definitions/SubWorkflowStep" },
32
+ { "$ref": "#/definitions/StepsStep" },
53
33
  { "$ref": "#/definitions/WaitStep" },
54
34
  { "$ref": "#/definitions/DispatchStep" }
55
35
  ]
56
36
  },
57
37
  "TaskStep": {
58
38
  "type": "object",
59
- "required": ["id"],
39
+ "required": ["step_id", "type", "service"],
60
40
  "additionalProperties": true,
61
41
  "properties": {
62
- "id": {
63
- "type": "string",
64
- "minLength": 1
65
- },
66
- "type": {
67
- "type": "string",
68
- "const": "task"
69
- },
70
- "name": {
71
- "type": "string"
72
- },
73
- "service": {
74
- "type": "string",
75
- "description": "Target service name"
76
- },
77
- "operation": {
78
- "type": "string",
79
- "description": "Operation identifier"
80
- },
81
- "action": {
82
- "type": "string"
83
- },
84
- "endpoint": {
85
- "type": "string"
86
- },
87
- "input": {
88
- "type": "object"
89
- },
90
- "output": {
91
- "type": "object"
92
- },
93
- "depends_on": {
94
- "type": "array",
95
- "items": {
96
- "type": "string"
97
- }
98
- },
99
- "retry": {
100
- "$ref": "#/definitions/Retry"
101
- },
102
- "timeoutMs": {
103
- "type": "integer",
104
- "minimum": 1
105
- }
42
+ "step_id": { "type": "string", "minLength": 1 },
43
+ "type": { "type": "string", "const": "task" },
44
+ "service": { "type": "string" },
45
+ "operation": { "type": "string" },
46
+ "input": { "type": "object" },
47
+ "output": { "type": "object" },
48
+ "depends_on": { "type": "array", "items": { "type": "string" } },
49
+ "retry": { "$ref": "#/definitions/Retry" },
50
+ "timeoutMs": { "type": "integer", "minimum": 1 }
106
51
  }
107
52
  },
108
53
  "ForeachStep": {
109
54
  "type": "object",
110
- "required": ["id", "iterator", "body"],
55
+ "required": ["step_id", "type", "iterator", "body"],
111
56
  "additionalProperties": true,
112
57
  "properties": {
113
- "id": {
114
- "type": "string",
115
- "minLength": 1
116
- },
117
- "type": {
118
- "type": "string",
119
- "const": "foreach"
120
- },
121
- "name": {
122
- "type": "string"
123
- },
124
- "iterator": {
125
- "type": "string"
126
- },
127
- "body": {
128
- "type": "array",
129
- "minItems": 1,
130
- "items": {
131
- "$ref": "#/definitions/Step"
132
- }
133
- },
134
- "output": {
135
- "type": "object"
136
- },
137
- "service": {
138
- "deprecated": true,
139
- "description": "DEPRECATED: This field should not be used in foreach steps"
140
- },
141
- "input": {
142
- "deprecated": true,
143
- "description": "DEPRECATED: This field should not be used in foreach steps"
144
- }
58
+ "step_id": { "type": "string", "minLength": 1 },
59
+ "type": { "type": "string", "const": "foreach" },
60
+ "iterator": { "type": "string", "minLength": 1 },
61
+ "body": { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/Step" } },
62
+ "output": { "type": "object" }
145
63
  }
146
64
  },
147
65
  "ForkJoinStep": {
148
66
  "type": "object",
149
- "required": ["id", "branches"],
67
+ "required": ["step_id", "type", "branches", "join"],
150
68
  "additionalProperties": true,
151
69
  "properties": {
152
- "id": {
153
- "type": "string",
154
- "minLength": 1
155
- },
156
- "type": {
157
- "type": "string",
158
- "const": "fork_join"
159
- },
160
- "name": {
161
- "type": "string"
162
- },
70
+ "step_id": { "type": "string", "minLength": 1 },
71
+ "type": { "type": "string", "const": "fork_join" },
163
72
  "branches": {
164
- "type": "array",
165
- "minItems": 1,
166
- "items": {
167
- "$ref": "#/definitions/Step"
168
- }
169
- },
170
- "join": {
171
73
  "type": "object",
172
- "properties": {
173
- "strategy": {
174
- "type": "string",
175
- "enum": ["merge", "first", "last", "all", "custom"],
176
- "default": "merge"
177
- },
178
- "output": {
179
- "type": "object"
180
- }
181
- }
182
- },
183
- "service": {
184
- "deprecated": true,
185
- "description": "DEPRECATED: This field should not be used in fork_join steps"
74
+ "minProperties": 1,
75
+ "additionalProperties": { "$ref": "#/definitions/Step" }
186
76
  },
187
- "input": {
188
- "deprecated": true,
189
- "description": "DEPRECATED: This field should not be used in fork_join steps"
190
- }
77
+ "join": { "type": "object" }
191
78
  }
192
79
  },
193
80
  "SwitchStep": {
194
81
  "type": "object",
195
- "required": ["id", "expression", "cases"],
82
+ "required": ["step_id", "type", "expression", "cases"],
196
83
  "additionalProperties": true,
197
84
  "properties": {
198
- "id": {
199
- "type": "string",
200
- "minLength": 1
201
- },
202
- "type": {
203
- "type": "string",
204
- "const": "switch"
205
- },
206
- "name": {
207
- "type": "string"
208
- },
209
- "expression": {
210
- "type": "string"
211
- },
212
- "cases": {
213
- "type": "object",
214
- "minProperties": 1,
215
- "additionalProperties": {
216
- "$ref": "#/definitions/Step"
217
- }
218
- },
219
- "default": {
220
- "$ref": "#/definitions/Step",
221
- "description": "Recommended: Always provide a default case"
222
- },
223
- "depends_on": {
224
- "type": "array",
225
- "items": {
226
- "type": "string"
227
- }
228
- },
229
- "service": {
230
- "deprecated": true,
231
- "description": "DEPRECATED: This field should not be used in switch steps"
232
- }
85
+ "step_id": { "type": "string", "minLength": 1 },
86
+ "type": { "type": "string", "const": "switch" },
87
+ "expression": { "type": "string", "minLength": 1 },
88
+ "cases": { "type": "object", "minProperties": 1, "additionalProperties": { "$ref": "#/definitions/Step" } },
89
+ "default": { "$ref": "#/definitions/Step" }
233
90
  }
234
91
  },
235
- "SubWorkflowStep": {
92
+ "StepsStep": {
236
93
  "type": "object",
237
- "required": ["id", "steps"],
94
+ "required": ["step_id", "type", "steps"],
238
95
  "additionalProperties": true,
239
96
  "properties": {
240
- "id": {
241
- "type": "string",
242
- "minLength": 1
243
- },
244
- "type": {
245
- "type": "string",
246
- "const": "sub_workflow"
247
- },
248
- "name": {
249
- "type": "string"
250
- },
251
- "input": {
252
- "type": "object"
253
- },
254
- "steps": {
255
- "type": "array",
256
- "minItems": 1,
257
- "items": {
258
- "$ref": "#/definitions/Step"
259
- }
260
- }
97
+ "step_id": { "type": "string", "minLength": 1 },
98
+ "type": { "type": "string", "const": "steps" },
99
+ "steps": { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/Step" } },
100
+ "max_depth": { "type": "integer", "minimum": 1, "maximum": 10 }
261
101
  }
262
102
  },
263
103
  "WaitStep": {
264
104
  "type": "object",
265
- "required": ["id", "durationMs"],
105
+ "required": ["step_id", "type", "durationMs"],
266
106
  "additionalProperties": true,
267
107
  "properties": {
268
- "id": {
269
- "type": "string",
270
- "minLength": 1
271
- },
272
- "type": {
273
- "type": "string",
274
- "const": "wait"
275
- },
276
- "name": {
277
- "type": "string"
278
- },
279
- "durationMs": {
280
- "type": "integer",
281
- "minimum": 0
282
- },
283
- "service": {
284
- "deprecated": true,
285
- "description": "DEPRECATED: This field should not be used in wait steps"
286
- },
287
- "input": {
288
- "deprecated": true,
289
- "description": "DEPRECATED: This field should not be used in wait steps"
290
- },
291
- "output": {
292
- "deprecated": true,
293
- "description": "DEPRECATED: This field should not be used in wait steps"
294
- }
108
+ "step_id": { "type": "string", "minLength": 1 },
109
+ "type": { "type": "string", "const": "wait" },
110
+ "durationMs": { "type": "integer", "minimum": 0 }
295
111
  }
296
112
  },
297
113
  "DispatchStep": {
298
114
  "type": "object",
299
- "required": ["id", "target"],
115
+ "required": ["step_id", "type", "method", "target"],
300
116
  "additionalProperties": true,
301
117
  "properties": {
302
- "id": {
303
- "type": "string",
304
- "minLength": 1
305
- },
306
- "type": {
307
- "type": "string",
308
- "const": "dispatch"
309
- },
310
- "name": {
311
- "type": "string"
312
- },
313
- "method": {
314
- "type": "string",
315
- "enum": ["webhook", "http", "grpc", "kafka", "sqs", "custom"],
316
- "default": "webhook"
317
- },
318
- "target": {
319
- "type": "string"
320
- },
321
- "input": {
322
- "type": "object"
323
- },
324
- "headers": {
325
- "type": "object"
326
- },
327
- "retry": {
328
- "$ref": "#/definitions/Retry"
329
- }
118
+ "step_id": { "type": "string", "minLength": 1 },
119
+ "type": { "type": "string", "const": "dispatch" },
120
+ "method": { "type": "string" },
121
+ "target": { "type": "string" }
330
122
  }
331
123
  },
332
124
  "Retry": {
333
125
  "type": "object",
334
- "additionalProperties": true,
126
+ "required": ["maxAttempts", "delayMs"],
127
+ "additionalProperties": false,
335
128
  "properties": {
336
- "maxAttempts": {
337
- "type": "integer",
338
- "minimum": 1
339
- },
340
- "delayMs": {
341
- "type": "integer",
342
- "minimum": 0
343
- },
344
- "backoffMultiplier": {
345
- "type": "number",
346
- "minimum": 1.0
347
- }
129
+ "maxAttempts": { "type": "integer", "minimum": 1 },
130
+ "delayMs": { "type": "integer", "minimum": 0 }
348
131
  }
349
132
  }
350
133
  }
351
- }
134
+ }
@@ -1,417 +1,139 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "https://onlineapps.cz/schemas/cookbook-strict/v2.0.0",
4
- "title": "Cookbook Schema - Strict Mode",
5
- "description": "Strict validation schema for production environments with all safety checks enabled",
3
+ "$id": "https://onlineapps.cz/schemas/cookbook-strict/v2.1.0",
4
+ "title": "Cookbook Schema - Strict Mode (V2.1)",
5
+ "description": "Strict validation schema for V2.1 cookbooks (no additional properties on root).",
6
6
  "type": "object",
7
7
  "required": ["steps", "version"],
8
8
  "additionalProperties": false,
9
9
  "properties": {
10
- "name": {
11
- "type": "string",
12
- "minLength": 1,
13
- "maxLength": 100,
14
- "pattern": "^[a-zA-Z0-9-_]+$"
15
- },
16
- "version": {
17
- "type": "string",
18
- "pattern": "^\\d+\\.\\d+\\.\\d+$",
19
- "description": "Semantic version of the cookbook schema"
20
- },
21
- "description": {
22
- "type": "string",
23
- "maxLength": 500
24
- },
25
- "api_input": {
26
- "type": "object",
27
- "description": "Initial input data for the workflow"
28
- },
29
- "metadata": {
30
- "type": "object",
31
- "description": "Additional metadata for debugging and tracking",
32
- "properties": {
33
- "created_at": {
34
- "type": "string",
35
- "format": "date-time"
36
- },
37
- "author": {
38
- "type": "string"
39
- },
40
- "tags": {
41
- "type": "array",
42
- "items": {
43
- "type": "string"
44
- }
45
- }
46
- }
47
- },
10
+ "name": { "type": "string", "minLength": 1 },
11
+ "version": { "type": "string", "pattern": "^2\\.\\d+\\.\\d+$" },
12
+ "description": { "type": "string" },
13
+ "api_input": { "type": "object" },
14
+ "metadata": { "type": "object", "additionalProperties": true },
48
15
  "steps": {
49
16
  "type": "array",
50
17
  "minItems": 1,
51
- "maxItems": 1000,
52
- "items": {
53
- "$ref": "#/definitions/Step"
54
- }
55
- }
18
+ "items": { "$ref": "#/definitions/Step" }
19
+ },
20
+ "delivery": { "type": "object" }
56
21
  },
57
22
  "definitions": {
58
23
  "Step": {
59
24
  "type": "object",
60
- "required": ["id", "type"],
61
- "properties": {
62
- "id": {
63
- "type": "string",
64
- "minLength": 1,
65
- "maxLength": 100,
66
- "pattern": "^[a-zA-Z0-9-_]+$"
67
- },
68
- "type": {
69
- "type": "string",
70
- "enum": ["task", "foreach", "fork_join", "switch", "sub_workflow", "wait", "dispatch"]
71
- }
72
- },
25
+ "required": ["step_id", "type"],
73
26
  "oneOf": [
74
27
  { "$ref": "#/definitions/TaskStep" },
75
28
  { "$ref": "#/definitions/ForeachStep" },
76
29
  { "$ref": "#/definitions/ForkJoinStep" },
77
30
  { "$ref": "#/definitions/SwitchStep" },
78
- { "$ref": "#/definitions/SubWorkflowStep" },
31
+ { "$ref": "#/definitions/StepsStep" },
79
32
  { "$ref": "#/definitions/WaitStep" },
80
33
  { "$ref": "#/definitions/DispatchStep" }
81
34
  ]
82
35
  },
83
36
  "TaskStep": {
84
37
  "type": "object",
85
- "required": ["id", "type", "service"],
86
- "additionalProperties": false,
38
+ "required": ["step_id", "type", "service"],
39
+ "additionalProperties": true,
87
40
  "properties": {
88
- "id": {
89
- "type": "string",
90
- "minLength": 1,
91
- "maxLength": 100,
92
- "pattern": "^[a-zA-Z0-9-_]+$"
93
- },
94
- "type": {
95
- "type": "string",
96
- "const": "task"
97
- },
98
- "name": {
99
- "type": "string",
100
- "minLength": 1,
101
- "maxLength": 200
102
- },
103
- "service": {
104
- "type": "string",
105
- "minLength": 1,
106
- "maxLength": 100,
107
- "pattern": "^[a-zA-Z0-9-_]+$"
108
- },
109
- "operation": {
110
- "type": "string",
111
- "minLength": 1,
112
- "maxLength": 100,
113
- "description": "Operation identifier for the service to execute"
114
- },
115
- "action": {
116
- "type": "string",
117
- "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"]
118
- },
119
- "endpoint": {
120
- "type": "string",
121
- "minLength": 1,
122
- "maxLength": 500
123
- },
124
- "input": {
125
- "type": "object",
126
- "additionalProperties": {
127
- "type": "string",
128
- "minLength": 1
129
- },
130
- "description": "Optional input mapping"
131
- },
132
- "output": {
133
- "type": "object",
134
- "additionalProperties": {
135
- "type": "string",
136
- "minLength": 1
137
- },
138
- "description": "Optional output mapping"
139
- },
140
- "depends_on": {
141
- "type": "array",
142
- "items": {
143
- "type": "string",
144
- "minLength": 1,
145
- "maxLength": 100
146
- },
147
- "maxItems": 100
148
- },
149
- "retry": {
150
- "$ref": "#/definitions/Retry"
151
- },
152
- "timeoutMs": {
153
- "type": "integer",
154
- "minimum": 1,
155
- "maximum": 3600000
156
- }
41
+ "step_id": { "type": "string", "minLength": 1, "pattern": "^[a-zA-Z0-9_]+$" },
42
+ "type": { "type": "string", "const": "task" },
43
+ "name": { "type": "string", "minLength": 1 },
44
+ "service": { "type": "string", "minLength": 1 },
45
+ "operation": { "type": "string", "minLength": 1 },
46
+ "input": { "type": "object", "additionalProperties": true },
47
+ "output": { "type": "object", "additionalProperties": true },
48
+ "depends_on": { "type": "array", "items": { "type": "string" } },
49
+ "retry": { "$ref": "#/definitions/Retry" },
50
+ "timeoutMs": { "type": "integer", "minimum": 1 },
51
+ "on_error": { "$ref": "#/definitions/ErrorHandler" }
157
52
  }
158
53
  },
159
54
  "ForeachStep": {
160
55
  "type": "object",
161
- "required": ["id", "type", "iterator", "body"],
162
- "additionalProperties": false,
56
+ "required": ["step_id", "type", "iterator", "body"],
57
+ "additionalProperties": true,
163
58
  "properties": {
164
- "id": {
165
- "type": "string",
166
- "minLength": 1,
167
- "maxLength": 100,
168
- "pattern": "^[a-zA-Z0-9-_]+$"
169
- },
170
- "type": {
171
- "type": "string",
172
- "const": "foreach"
173
- },
174
- "name": {
175
- "type": "string",
176
- "minLength": 1,
177
- "maxLength": 200
178
- },
179
- "iterator": {
180
- "type": "string",
181
- "minLength": 1,
182
- "pattern": "^\\$"
183
- },
184
- "body": {
185
- "type": "array",
186
- "minItems": 1,
187
- "maxItems": 100,
188
- "items": {
189
- "$ref": "#/definitions/Step"
190
- }
191
- },
192
- "output": {
193
- "type": "object",
194
- "additionalProperties": {
195
- "type": "string",
196
- "minLength": 1
197
- }
198
- }
59
+ "step_id": { "type": "string", "minLength": 1, "pattern": "^[a-zA-Z0-9_]+$" },
60
+ "type": { "type": "string", "const": "foreach" },
61
+ "iterator": { "type": "string", "minLength": 1 },
62
+ "body": { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/Step" } },
63
+ "output": { "type": "object", "additionalProperties": true }
199
64
  }
200
65
  },
201
66
  "ForkJoinStep": {
202
67
  "type": "object",
203
- "required": ["id", "type", "branches", "join"],
204
- "additionalProperties": false,
68
+ "required": ["step_id", "type", "branches", "join"],
69
+ "additionalProperties": true,
205
70
  "properties": {
206
- "id": {
207
- "type": "string",
208
- "minLength": 1,
209
- "maxLength": 100,
210
- "pattern": "^[a-zA-Z0-9-_]+$"
211
- },
212
- "type": {
213
- "type": "string",
214
- "const": "fork_join"
215
- },
216
- "name": {
217
- "type": "string",
218
- "minLength": 1,
219
- "maxLength": 200
220
- },
71
+ "step_id": { "type": "string", "minLength": 1, "pattern": "^[a-zA-Z0-9_]+$" },
72
+ "type": { "type": "string", "const": "fork_join" },
221
73
  "branches": {
222
- "type": "array",
223
- "minItems": 1,
224
- "maxItems": 50,
225
- "items": {
226
- "$ref": "#/definitions/Step"
227
- }
74
+ "type": "object",
75
+ "minProperties": 1,
76
+ "additionalProperties": { "$ref": "#/definitions/Step" }
228
77
  },
229
78
  "join": {
230
79
  "type": "object",
231
80
  "required": ["strategy"],
232
- "additionalProperties": false,
233
81
  "properties": {
234
- "strategy": {
235
- "type": "string",
236
- "enum": ["merge", "first", "last", "all", "custom"],
237
- "description": "How to combine branch results"
238
- },
239
- "output": {
240
- "type": "object",
241
- "additionalProperties": {
242
- "type": "string",
243
- "minLength": 1
244
- }
245
- },
246
- "customHandler": {
247
- "type": "string",
248
- "description": "Custom handler name when strategy is 'custom'"
249
- }
82
+ "strategy": { "type": "string", "enum": ["merge", "first", "last", "all", "custom"] },
83
+ "output": { "type": "object" },
84
+ "timeoutMs": { "type": "integer", "minimum": 1 }
250
85
  }
251
86
  }
252
87
  }
253
88
  },
254
89
  "SwitchStep": {
255
90
  "type": "object",
256
- "required": ["id", "type", "expression", "cases", "default"],
257
- "additionalProperties": false,
91
+ "required": ["step_id", "type", "expression", "cases"],
92
+ "additionalProperties": true,
258
93
  "properties": {
259
- "id": {
260
- "type": "string",
261
- "minLength": 1,
262
- "maxLength": 100,
263
- "pattern": "^[a-zA-Z0-9-_]+$"
264
- },
265
- "type": {
266
- "type": "string",
267
- "const": "switch"
268
- },
269
- "name": {
270
- "type": "string",
271
- "minLength": 1,
272
- "maxLength": 200
273
- },
274
- "expression": {
275
- "type": "string",
276
- "minLength": 1,
277
- "pattern": "^\\$"
278
- },
94
+ "step_id": { "type": "string", "minLength": 1, "pattern": "^[a-zA-Z0-9_]+$" },
95
+ "type": { "type": "string", "const": "switch" },
96
+ "expression": { "type": "string", "minLength": 1 },
279
97
  "cases": {
280
98
  "type": "object",
281
99
  "minProperties": 1,
282
- "maxProperties": 100,
283
- "additionalProperties": {
284
- "$ref": "#/definitions/Step"
285
- }
286
- },
287
- "default": {
288
- "$ref": "#/definitions/Step"
100
+ "additionalProperties": { "$ref": "#/definitions/Step" }
289
101
  },
290
- "depends_on": {
291
- "type": "array",
292
- "items": {
293
- "type": "string",
294
- "minLength": 1,
295
- "maxLength": 100
296
- },
297
- "maxItems": 100
298
- }
102
+ "default": { "$ref": "#/definitions/Step" }
299
103
  }
300
104
  },
301
- "SubWorkflowStep": {
105
+ "StepsStep": {
302
106
  "type": "object",
303
- "required": ["id", "type", "steps"],
304
- "additionalProperties": false,
107
+ "required": ["step_id", "type", "steps"],
108
+ "additionalProperties": true,
305
109
  "properties": {
306
- "id": {
307
- "type": "string",
308
- "minLength": 1,
309
- "maxLength": 100,
310
- "pattern": "^[a-zA-Z0-9-_]+$"
311
- },
312
- "type": {
313
- "type": "string",
314
- "const": "sub_workflow"
315
- },
316
- "name": {
317
- "type": "string",
318
- "minLength": 1,
319
- "maxLength": 200
320
- },
321
- "input": {
322
- "type": "object",
323
- "additionalProperties": {
324
- "type": "string",
325
- "minLength": 1
326
- },
327
- "description": "Optional input for sub-workflow"
328
- },
329
- "steps": {
330
- "type": "array",
331
- "minItems": 1,
332
- "maxItems": 100,
333
- "items": {
334
- "$ref": "#/definitions/Step"
335
- }
336
- }
110
+ "step_id": { "type": "string", "minLength": 1, "pattern": "^[a-zA-Z0-9_]+$" },
111
+ "type": { "type": "string", "const": "steps" },
112
+ "steps": { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/Step" } },
113
+ "max_depth": { "type": "integer", "minimum": 1, "maximum": 10 }
337
114
  }
338
115
  },
339
116
  "WaitStep": {
340
117
  "type": "object",
341
- "required": ["id", "type", "durationMs"],
342
- "additionalProperties": false,
118
+ "required": ["step_id", "type", "durationMs"],
119
+ "additionalProperties": true,
343
120
  "properties": {
344
- "id": {
345
- "type": "string",
346
- "minLength": 1,
347
- "maxLength": 100,
348
- "pattern": "^[a-zA-Z0-9-_]+$"
349
- },
350
- "type": {
351
- "type": "string",
352
- "const": "wait"
353
- },
354
- "name": {
355
- "type": "string",
356
- "minLength": 1,
357
- "maxLength": 200
358
- },
359
- "durationMs": {
360
- "type": "integer",
361
- "minimum": 0,
362
- "maximum": 86400000,
363
- "description": "Wait duration in milliseconds (max 24 hours)"
364
- }
121
+ "step_id": { "type": "string", "minLength": 1, "pattern": "^[a-zA-Z0-9_]+$" },
122
+ "type": { "type": "string", "const": "wait" },
123
+ "durationMs": { "type": "integer", "minimum": 0 }
365
124
  }
366
125
  },
367
126
  "DispatchStep": {
368
127
  "type": "object",
369
- "required": ["id", "type", "method", "target"],
370
- "additionalProperties": false,
128
+ "required": ["step_id", "type", "method", "target"],
129
+ "additionalProperties": true,
371
130
  "properties": {
372
- "id": {
373
- "type": "string",
374
- "minLength": 1,
375
- "maxLength": 100,
376
- "pattern": "^[a-zA-Z0-9-_]+$"
377
- },
378
- "type": {
379
- "type": "string",
380
- "const": "dispatch"
381
- },
382
- "name": {
383
- "type": "string",
384
- "minLength": 1,
385
- "maxLength": 200
386
- },
387
- "method": {
388
- "type": "string",
389
- "enum": ["webhook", "http", "grpc", "kafka", "sqs", "custom"],
390
- "description": "Dispatch method to use"
391
- },
392
- "target": {
393
- "type": "string",
394
- "format": "uri",
395
- "maxLength": 2000
396
- },
397
- "input": {
398
- "type": "object",
399
- "additionalProperties": {
400
- "type": "string",
401
- "minLength": 1
402
- },
403
- "description": "Optional dispatch payload"
404
- },
405
- "headers": {
406
- "type": "object",
407
- "additionalProperties": {
408
- "type": "string"
409
- },
410
- "description": "Optional HTTP headers for webhook/http methods"
411
- },
412
- "retry": {
413
- "$ref": "#/definitions/Retry"
414
- }
131
+ "step_id": { "type": "string", "minLength": 1, "pattern": "^[a-zA-Z0-9_]+$" },
132
+ "type": { "type": "string", "const": "dispatch" },
133
+ "method": { "type": "string", "enum": ["webhook", "http", "grpc", "kafka", "sqs", "custom"] },
134
+ "target": { "type": "string", "format": "uri" },
135
+ "input": { "type": "object" },
136
+ "retry": { "$ref": "#/definitions/Retry" }
415
137
  }
416
138
  },
417
139
  "Retry": {
@@ -419,23 +141,23 @@
419
141
  "required": ["maxAttempts", "delayMs"],
420
142
  "additionalProperties": false,
421
143
  "properties": {
422
- "maxAttempts": {
423
- "type": "integer",
424
- "minimum": 1,
425
- "maximum": 10
426
- },
427
- "delayMs": {
428
- "type": "integer",
429
- "minimum": 0,
430
- "maximum": 300000
431
- },
432
- "backoffMultiplier": {
433
- "type": "number",
434
- "minimum": 1.0,
435
- "maximum": 5.0,
436
- "description": "Exponential backoff multiplier"
437
- }
144
+ "maxAttempts": { "type": "integer", "minimum": 1 },
145
+ "delayMs": { "type": "integer", "minimum": 0 },
146
+ "backoffMultiplier": { "type": "number", "minimum": 1.0, "maximum": 5.0 }
147
+ }
148
+ },
149
+ "ErrorHandler": {
150
+ "type": "object",
151
+ "required": ["strategy"],
152
+ "additionalProperties": false,
153
+ "properties": {
154
+ "strategy": { "type": "string", "enum": ["retry", "fail", "continue", "compensate", "fallback"] },
155
+ "retries": { "type": "integer", "minimum": 0, "maximum": 10 },
156
+ "retryDelayMs": { "type": "integer", "minimum": 0 },
157
+ "fallbackStep": { "type": "string" },
158
+ "compensationStep": { "type": "string" },
159
+ "errorOutput": { "type": "string" }
438
160
  }
439
161
  }
440
162
  }
441
- }
163
+ }
@@ -5,7 +5,7 @@
5
5
  * Validates:
6
6
  * - Top-level structure against JSON Schema (strict: no additional properties)
7
7
  * - Recursive validation of each Step object, ensuring all required fields are present and no extraneous fields
8
- * - Unique `id` across all steps (including nested steps in foreach, fork_join, switch, sub_workflow)
8
+ * - Unique `step_id` across all steps (including nested steps in foreach, fork_join, switch, steps)
9
9
  * - For TaskStep and DispatchStep: `input` and `output` objects each have at least one mapping, all values are non-empty strings
10
10
  * - For Retry configurations: `maxAttempts` ≥ 1, `delayMs` ≥ 0
11
11
  * - For ForeachStep: `iterator` is a non-empty string, `body` is a non-empty array of valid Steps, optional `output` maps have non-empty string values
@@ -35,8 +35,7 @@ const loadSchemaFile = (filename) => {
35
35
  if (fs.existsSync(schemaPath)) {
36
36
  return JSON.parse(fs.readFileSync(schemaPath, 'utf-8'));
37
37
  }
38
- // Fallback to default v2 schema
39
- return JSON.parse(fs.readFileSync(path.join(__dirname, '../../schemas/cookbook.v2.schema.json'), 'utf-8'));
38
+ throw new Error(`[cookbook-core] Schema file not found - Expected ${schemaPath}`);
40
39
  };
41
40
 
42
41
  const cookbookSchema = loadSchemaFile('cookbook.v2.schema.json');
@@ -150,18 +149,18 @@ function collectAndValidateUniqueIds(steps, seenIds, path) {
150
149
  if (typeof step !== 'object' || step === null) {
151
150
  throw new CookbookValidationError(`${context} must be an object.`);
152
151
  }
153
- // id must exist and be a string
154
- if (!('id' in step)) {
155
- throw new CookbookValidationError(`${context} is missing required property 'id'.`);
152
+ // step_id must exist and be a string
153
+ if (!('step_id' in step)) {
154
+ throw new CookbookValidationError(`${context} is missing required property 'step_id'.`);
156
155
  }
157
- assertNonEmptyString(step.id, `${context}.id`);
158
- if (seenIds.has(step.id)) {
159
- throw new CookbookValidationError(`Duplicate step ID '${step.id}' found at ${context}.`);
156
+ assertNonEmptyString(step.step_id, `${context}.step_id`);
157
+ if (seenIds.has(step.step_id)) {
158
+ throw new CookbookValidationError(`Duplicate step_id '${step.step_id}' found at ${context}.`);
160
159
  }
161
- seenIds.add(step.id);
160
+ seenIds.add(step.step_id);
162
161
 
163
162
  // Recurse into nested steps according to type
164
- const stepType = step.type || 'task';
163
+ const stepType = step.type;
165
164
  switch (stepType) {
166
165
  case 'foreach':
167
166
  if (!Array.isArray(step.body)) {
@@ -170,10 +169,12 @@ function collectAndValidateUniqueIds(steps, seenIds, path) {
170
169
  collectAndValidateUniqueIds(step.body, seenIds, `${context}.body`);
171
170
  break;
172
171
  case 'fork_join':
173
- if (!Array.isArray(step.branches)) {
174
- throw new CookbookValidationError(`${context} of type 'fork_join' must have 'branches' as an array.`);
172
+ if (typeof step.branches !== 'object' || step.branches === null || Array.isArray(step.branches)) {
173
+ throw new CookbookValidationError(`${context} of type 'fork_join' must have 'branches' as an object.`);
175
174
  }
176
- collectAndValidateUniqueIds(step.branches, seenIds, `${context}.branches`);
175
+ Object.entries(step.branches).forEach(([branchKey, branchStep]) => {
176
+ collectAndValidateUniqueIds([branchStep], seenIds, `${context}.branches['${branchKey}']`);
177
+ });
177
178
  break;
178
179
  case 'switch':
179
180
  // cases: object mapping keys to Step
@@ -186,13 +187,13 @@ function collectAndValidateUniqueIds(steps, seenIds, path) {
186
187
  collectAndValidateUniqueIds([step.default], seenIds, `${context}.default`);
187
188
  }
188
189
  break;
189
- case 'sub_workflow':
190
+ case 'steps':
190
191
  if (!Array.isArray(step.steps)) {
191
- throw new CookbookValidationError(`${context} of type 'sub_workflow' must have 'steps' as an array.`);
192
+ throw new CookbookValidationError(`${context} of type 'steps' must have 'steps' as an array.`);
192
193
  }
193
194
  collectAndValidateUniqueIds(step.steps, seenIds, `${context}.steps`);
194
195
  break;
195
- // task, wait, dispatch have no nested steps for id collection
196
+ // task, wait, dispatch have no nested steps for step_id collection
196
197
  case 'task':
197
198
  case 'wait':
198
199
  case 'dispatch':
@@ -215,20 +216,19 @@ function validateStep(step, context, mode = 'default') {
215
216
  throw new CookbookValidationError(`${context} must be an object.`);
216
217
  }
217
218
 
218
- // Validate required properties: id is required, type is optional (defaults to 'task')
219
- if (!('id' in step)) {
220
- throw new CookbookValidationError(`${context} is missing required property 'id'.`);
219
+ // V2.1: step_id and type are required
220
+ if (!('step_id' in step)) {
221
+ throw new CookbookValidationError(`${context} is missing required property 'step_id'.`);
221
222
  }
222
- assertNonEmptyString(step.id, `${context}.id`);
223
-
224
- // Type is optional, defaults to 'task' for simple service calls
225
- const stepType = step.type || 'task';
226
- if (step.type) {
227
- assertNonEmptyString(step.type, `${context}.type`);
223
+ assertNonEmptyString(step.step_id, `${context}.step_id`);
224
+ if (!('type' in step)) {
225
+ throw new CookbookValidationError(`${context} is missing required property 'type'.`);
228
226
  }
227
+ assertNonEmptyString(step.type, `${context}.type`);
228
+ const stepType = step.type;
229
229
 
230
- // Ensure no additional properties beyond { id, type, and those allowed per type }
231
- const commonAllowed = new Set(['id', 'type']);
230
+ // Ensure no additional properties beyond { step_id, type, and those allowed per type }
231
+ const commonAllowed = new Set(['step_id', 'type']);
232
232
  let allowedProps = new Set(commonAllowed);
233
233
 
234
234
  switch (stepType) {
@@ -403,14 +403,11 @@ function validateStep(step, context, mode = 'default') {
403
403
  allowedProps = new Set([...commonAllowed, 'name', 'expression', 'cases', 'default', 'depends_on']);
404
404
  break;
405
405
  }
406
- case 'sub_workflow': {
406
+ case 'steps': {
407
407
  // Required: steps (input now optional)
408
- const req = ['steps'];
409
- req.forEach((prop) => {
410
- if (!(prop in step)) {
411
- throw new CookbookValidationError(`${context} of type 'sub_workflow' is missing required property '${prop}'.`);
412
- }
413
- });
408
+ if (!('steps' in step)) {
409
+ throw new CookbookValidationError(`${context} of type 'steps' is missing required property 'steps'.`);
410
+ }
414
411
  // Validate input map if present
415
412
  if ('input' in step) {
416
413
  assertNonEmptyStringMap(step.input, `${context}.input`, true);
@@ -428,8 +425,13 @@ function validateStep(step, context, mode = 'default') {
428
425
  if ('name' in step) {
429
426
  assertNonEmptyString(step.name, `${context}.name`);
430
427
  }
428
+ if ('max_depth' in step) {
429
+ if (!Number.isInteger(step.max_depth) || step.max_depth < 1) {
430
+ throw new CookbookValidationError(`${context}.max_depth must be an integer ≥ 1.`);
431
+ }
432
+ }
431
433
 
432
- allowedProps = new Set([...commonAllowed, 'name', 'input', 'steps']);
434
+ allowedProps = new Set([...commonAllowed, 'name', 'input', 'steps', 'max_depth']);
433
435
  break;
434
436
  }
435
437
  case 'wait': {
@@ -15,8 +15,8 @@ function collectAllStepIds(steps, ids = new Set()) {
15
15
  if (!Array.isArray(steps)) return ids;
16
16
 
17
17
  steps.forEach(step => {
18
- if (step && typeof step === 'object' && step.id) {
19
- ids.add(step.id);
18
+ if (step && typeof step === 'object' && step.step_id) {
19
+ ids.add(step.step_id);
20
20
 
21
21
  // Recursively collect from nested structures
22
22
  switch (step.type) {
@@ -25,7 +25,9 @@ function collectAllStepIds(steps, ids = new Set()) {
25
25
  break;
26
26
 
27
27
  case 'fork_join':
28
- if (step.branches) collectAllStepIds(step.branches, ids);
28
+ if (step.branches && typeof step.branches === 'object') {
29
+ Object.values(step.branches).forEach(branchStep => collectAllStepIds([branchStep], ids));
30
+ }
29
31
  break;
30
32
 
31
33
  case 'switch':
@@ -37,7 +39,7 @@ function collectAllStepIds(steps, ids = new Set()) {
37
39
  if (step.default) collectAllStepIds([step.default], ids);
38
40
  break;
39
41
 
40
- case 'sub_workflow':
42
+ case 'steps':
41
43
  if (step.steps) collectAllStepIds(step.steps, ids);
42
44
  break;
43
45
  }
@@ -83,7 +85,11 @@ function validateDependsOnReferences(cookbook) {
83
85
  break;
84
86
 
85
87
  case 'fork_join':
86
- if (step.branches) checkDependsOn(step.branches, `${stepPath}.branches`);
88
+ if (step.branches && typeof step.branches === 'object') {
89
+ Object.entries(step.branches).forEach(([branchKey, branchStep]) => {
90
+ checkDependsOn([branchStep], `${stepPath}.branches['${branchKey}']`);
91
+ });
92
+ }
87
93
  break;
88
94
 
89
95
  case 'switch':
@@ -95,7 +101,7 @@ function validateDependsOnReferences(cookbook) {
95
101
  if (step.default) checkDependsOn([step.default], `${stepPath}.default`);
96
102
  break;
97
103
 
98
- case 'sub_workflow':
104
+ case 'steps':
99
105
  if (step.steps) checkDependsOn(step.steps, `${stepPath}.steps`);
100
106
  break;
101
107
  }
@@ -187,8 +193,10 @@ function validateVariableReferences(cookbook) {
187
193
  break;
188
194
 
189
195
  case 'fork_join':
190
- if (step.branches) {
191
- step.branches.forEach((s, i) => checkStep(s, `${path}.branches[${i}]`));
196
+ if (step.branches && typeof step.branches === 'object') {
197
+ Object.entries(step.branches).forEach(([branchKey, s]) => {
198
+ checkStep(s, `${path}.branches['${branchKey}']`);
199
+ });
192
200
  }
193
201
  break;
194
202
 
@@ -203,7 +211,7 @@ function validateVariableReferences(cookbook) {
203
211
  }
204
212
  break;
205
213
 
206
- case 'sub_workflow':
214
+ case 'steps':
207
215
  if (step.steps) {
208
216
  step.steps.forEach((s, i) => checkStep(s, `${path}.steps[${i}]`));
209
217
  }
@@ -268,8 +276,10 @@ function validateErrorHandlerReferences(cookbook) {
268
276
  break;
269
277
 
270
278
  case 'fork_join':
271
- if (step.branches) {
272
- step.branches.forEach((s, i) => checkStep(s, `${path}.branches[${i}]`));
279
+ if (step.branches && typeof step.branches === 'object') {
280
+ Object.entries(step.branches).forEach(([branchKey, s]) => {
281
+ checkStep(s, `${path}.branches['${branchKey}']`);
282
+ });
273
283
  }
274
284
  break;
275
285
 
@@ -284,7 +294,7 @@ function validateErrorHandlerReferences(cookbook) {
284
294
  }
285
295
  break;
286
296
 
287
- case 'sub_workflow':
297
+ case 'steps':
288
298
  if (step.steps) {
289
299
  step.steps.forEach((s, i) => checkStep(s, `${path}.steps[${i}]`));
290
300
  }