soustack 0.1.3 → 0.2.3

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/src/schema.json CHANGED
@@ -1,11 +1,20 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "http://soustack.org/schema/v0.1",
4
- "title": "Soustack Recipe Schema v0.1",
3
+ "$id": "http://soustack.org/schema/v0.2.1",
4
+ "title": "Soustack Recipe Schema v0.2.1",
5
5
  "description": "A portable, scalable, interoperable recipe format.",
6
6
  "type": "object",
7
7
  "required": ["name", "ingredients", "instructions"],
8
+ "additionalProperties": false,
9
+ "patternProperties": {
10
+ "^x-": {}
11
+ },
8
12
  "properties": {
13
+ "$schema": {
14
+ "type": "string",
15
+ "format": "uri",
16
+ "description": "Optional schema hint for tooling compatibility"
17
+ },
9
18
  "id": {
10
19
  "type": "string",
11
20
  "description": "Unique identifier (slug or UUID)"
@@ -14,10 +23,19 @@
14
23
  "type": "string",
15
24
  "description": "The title of the recipe"
16
25
  },
26
+ "title": {
27
+ "type": "string",
28
+ "description": "Optional display title; alias for name"
29
+ },
17
30
  "version": {
18
31
  "type": "string",
19
32
  "pattern": "^\\d+\\.\\d+\\.\\d+$",
20
- "description": "Semantic versioning (e.g., 1.0.0)"
33
+ "description": "DEPRECATED: use recipeVersion for authoring revisions"
34
+ },
35
+ "recipeVersion": {
36
+ "type": "string",
37
+ "pattern": "^\\d+\\.\\d+\\.\\d+$",
38
+ "description": "Recipe content revision (semantic versioning, e.g., 1.0.0)"
21
39
  },
22
40
  "description": {
23
41
  "type": "string"
@@ -31,13 +49,31 @@
31
49
  "items": { "type": "string" }
32
50
  },
33
51
  "image": {
34
- "type": "string",
35
- "format": "uri"
52
+ "description": "Recipe-level hero image(s)",
53
+ "anyOf": [
54
+ {
55
+ "type": "string",
56
+ "format": "uri"
57
+ },
58
+ {
59
+ "type": "array",
60
+ "minItems": 1,
61
+ "items": {
62
+ "type": "string",
63
+ "format": "uri"
64
+ }
65
+ }
66
+ ]
36
67
  },
37
68
  "dateAdded": {
38
69
  "type": "string",
39
70
  "format": "date-time"
40
71
  },
72
+ "metadata": {
73
+ "type": "object",
74
+ "additionalProperties": true,
75
+ "description": "Free-form vendor metadata"
76
+ },
41
77
  "source": {
42
78
  "type": "object",
43
79
  "properties": {
@@ -97,24 +133,16 @@
97
133
  }
98
134
  },
99
135
  "time": {
100
- "oneOf": [
101
- {
102
- "type": "object",
103
- "properties": {
104
- "prep": { "type": "number" },
105
- "active": { "type": "number" },
106
- "passive": { "type": "number" },
107
- "total": { "type": "number" }
108
- }
109
- },
110
- {
111
- "type": "object",
112
- "properties": {
113
- "prepTime": { "type": "string" },
114
- "cookTime": { "type": "string" }
115
- }
116
- }
117
- ]
136
+ "type": "object",
137
+ "properties": {
138
+ "prep": { "type": "number" },
139
+ "active": { "type": "number" },
140
+ "passive": { "type": "number" },
141
+ "total": { "type": "number" },
142
+ "prepTime": { "type": "string", "format": "duration" },
143
+ "cookTime": { "type": "string", "format": "duration" }
144
+ },
145
+ "minProperties": 1
118
146
  },
119
147
  "quantity": {
120
148
  "type": "object",
@@ -197,6 +225,11 @@
197
225
  "properties": {
198
226
  "id": { "type": "string" },
199
227
  "text": { "type": "string" },
228
+ "image": {
229
+ "type": "string",
230
+ "format": "uri",
231
+ "description": "Optional image that illustrates this instruction"
232
+ },
200
233
  "destination": { "type": "string" },
201
234
  "dependsOn": {
202
235
  "type": "array",
@@ -210,7 +243,13 @@
210
243
  "type": "object",
211
244
  "required": ["duration", "type"],
212
245
  "properties": {
213
- "duration": { "type": "number" },
246
+ "duration": {
247
+ "anyOf": [
248
+ { "type": "number" },
249
+ { "type": "string", "pattern": "^P" }
250
+ ],
251
+ "description": "Minutes as a number or ISO8601 duration string"
252
+ },
214
253
  "type": { "type": "string", "enum": ["active", "passive"] },
215
254
  "scaling": { "type": "string", "enum": ["linear", "fixed", "sqrt"] }
216
255
  }
@@ -0,0 +1,344 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "http://soustack.org/schema/v0.2.1",
4
+ "title": "Soustack Recipe Schema v0.2.1",
5
+ "description": "A portable, scalable, interoperable recipe format.",
6
+ "type": "object",
7
+ "required": ["name", "ingredients", "instructions"],
8
+ "additionalProperties": false,
9
+ "patternProperties": {
10
+ "^x-": {}
11
+ },
12
+ "properties": {
13
+ "$schema": {
14
+ "type": "string",
15
+ "format": "uri",
16
+ "description": "Optional schema hint for tooling compatibility"
17
+ },
18
+ "id": {
19
+ "type": "string",
20
+ "description": "Unique identifier (slug or UUID)"
21
+ },
22
+ "name": {
23
+ "type": "string",
24
+ "description": "The title of the recipe"
25
+ },
26
+ "title": {
27
+ "type": "string",
28
+ "description": "Optional display title; alias for name"
29
+ },
30
+ "version": {
31
+ "type": "string",
32
+ "pattern": "^\\d+\\.\\d+\\.\\d+$",
33
+ "description": "DEPRECATED: use recipeVersion for authoring revisions"
34
+ },
35
+ "recipeVersion": {
36
+ "type": "string",
37
+ "pattern": "^\\d+\\.\\d+\\.\\d+$",
38
+ "description": "Recipe content revision (semantic versioning, e.g., 1.0.0)"
39
+ },
40
+ "description": {
41
+ "type": "string"
42
+ },
43
+ "category": {
44
+ "type": "string",
45
+ "examples": ["Main Course", "Dessert"]
46
+ },
47
+ "tags": {
48
+ "type": "array",
49
+ "items": { "type": "string" }
50
+ },
51
+ "image": {
52
+ "description": "Recipe-level hero image(s)",
53
+ "anyOf": [
54
+ {
55
+ "type": "string",
56
+ "format": "uri"
57
+ },
58
+ {
59
+ "type": "array",
60
+ "minItems": 1,
61
+ "items": {
62
+ "type": "string",
63
+ "format": "uri"
64
+ }
65
+ }
66
+ ]
67
+ },
68
+ "dateAdded": {
69
+ "type": "string",
70
+ "format": "date-time"
71
+ },
72
+ "metadata": {
73
+ "type": "object",
74
+ "additionalProperties": true,
75
+ "description": "Free-form vendor metadata"
76
+ },
77
+ "source": {
78
+ "type": "object",
79
+ "properties": {
80
+ "author": { "type": "string" },
81
+ "url": { "type": "string", "format": "uri" },
82
+ "name": { "type": "string" },
83
+ "adapted": { "type": "boolean" }
84
+ }
85
+ },
86
+ "yield": {
87
+ "$ref": "#/definitions/yield"
88
+ },
89
+ "time": {
90
+ "$ref": "#/definitions/time"
91
+ },
92
+ "equipment": {
93
+ "type": "array",
94
+ "items": { "$ref": "#/definitions/equipment" }
95
+ },
96
+ "ingredients": {
97
+ "type": "array",
98
+ "items": {
99
+ "anyOf": [
100
+ { "type": "string" },
101
+ { "$ref": "#/definitions/ingredient" },
102
+ { "$ref": "#/definitions/ingredientSubsection" }
103
+ ]
104
+ }
105
+ },
106
+ "instructions": {
107
+ "type": "array",
108
+ "items": {
109
+ "anyOf": [
110
+ { "type": "string" },
111
+ { "$ref": "#/definitions/instruction" },
112
+ { "$ref": "#/definitions/instructionSubsection" }
113
+ ]
114
+ }
115
+ },
116
+ "storage": {
117
+ "$ref": "#/definitions/storage"
118
+ },
119
+ "substitutions": {
120
+ "type": "array",
121
+ "items": { "$ref": "#/definitions/substitution" }
122
+ }
123
+ },
124
+ "definitions": {
125
+ "yield": {
126
+ "type": "object",
127
+ "required": ["amount", "unit"],
128
+ "properties": {
129
+ "amount": { "type": "number" },
130
+ "unit": { "type": "string" },
131
+ "servings": { "type": "number" },
132
+ "description": { "type": "string" }
133
+ }
134
+ },
135
+ "time": {
136
+ "type": "object",
137
+ "properties": {
138
+ "prep": { "type": "number" },
139
+ "active": { "type": "number" },
140
+ "passive": { "type": "number" },
141
+ "total": { "type": "number" },
142
+ "prepTime": { "type": "string", "format": "duration" },
143
+ "cookTime": { "type": "string", "format": "duration" }
144
+ },
145
+ "minProperties": 1
146
+ },
147
+ "quantity": {
148
+ "type": "object",
149
+ "required": ["amount"],
150
+ "properties": {
151
+ "amount": { "type": "number" },
152
+ "unit": { "type": ["string", "null"] }
153
+ }
154
+ },
155
+ "scaling": {
156
+ "type": "object",
157
+ "required": ["type"],
158
+ "properties": {
159
+ "type": {
160
+ "type": "string",
161
+ "enum": ["linear", "discrete", "proportional", "fixed", "bakers_percentage"]
162
+ },
163
+ "factor": { "type": "number" },
164
+ "referenceId": { "type": "string" },
165
+ "roundTo": { "type": "number" },
166
+ "min": { "type": "number" },
167
+ "max": { "type": "number" }
168
+ },
169
+ "if": {
170
+ "properties": { "type": { "const": "bakers_percentage" } }
171
+ },
172
+ "then": {
173
+ "required": ["referenceId"]
174
+ }
175
+ },
176
+ "ingredient": {
177
+ "type": "object",
178
+ "required": ["item"],
179
+ "properties": {
180
+ "id": { "type": "string" },
181
+ "item": { "type": "string" },
182
+ "quantity": { "$ref": "#/definitions/quantity" },
183
+ "name": { "type": "string" },
184
+ "aisle": { "type": "string" },
185
+ "prep": { "type": "string" },
186
+ "prepAction": { "type": "string" },
187
+ "prepTime": { "type": "number" },
188
+ "destination": { "type": "string" },
189
+ "scaling": { "$ref": "#/definitions/scaling" },
190
+ "critical": { "type": "boolean" },
191
+ "optional": { "type": "boolean" },
192
+ "notes": { "type": "string" }
193
+ }
194
+ },
195
+ "ingredientSubsection": {
196
+ "type": "object",
197
+ "required": ["subsection", "items"],
198
+ "properties": {
199
+ "subsection": { "type": "string" },
200
+ "items": {
201
+ "type": "array",
202
+ "items": { "$ref": "#/definitions/ingredient" }
203
+ }
204
+ }
205
+ },
206
+ "equipment": {
207
+ "type": "object",
208
+ "required": ["name"],
209
+ "properties": {
210
+ "id": { "type": "string" },
211
+ "name": { "type": "string" },
212
+ "required": { "type": "boolean" },
213
+ "label": { "type": "string" },
214
+ "capacity": { "$ref": "#/definitions/quantity" },
215
+ "scalingLimit": { "type": "number" },
216
+ "alternatives": {
217
+ "type": "array",
218
+ "items": { "type": "string" }
219
+ }
220
+ }
221
+ },
222
+ "instruction": {
223
+ "type": "object",
224
+ "required": ["text"],
225
+ "properties": {
226
+ "id": { "type": "string" },
227
+ "text": { "type": "string" },
228
+ "image": {
229
+ "type": "string",
230
+ "format": "uri",
231
+ "description": "Optional image that illustrates this instruction"
232
+ },
233
+ "destination": { "type": "string" },
234
+ "dependsOn": {
235
+ "type": "array",
236
+ "items": { "type": "string" }
237
+ },
238
+ "inputs": {
239
+ "type": "array",
240
+ "items": { "type": "string" }
241
+ },
242
+ "timing": {
243
+ "type": "object",
244
+ "required": ["duration", "type"],
245
+ "properties": {
246
+ "duration": {
247
+ "anyOf": [
248
+ { "type": "number" },
249
+ { "type": "string", "pattern": "^P" }
250
+ ],
251
+ "description": "Minutes as a number or ISO8601 duration string"
252
+ },
253
+ "type": { "type": "string", "enum": ["active", "passive"] },
254
+ "scaling": { "type": "string", "enum": ["linear", "fixed", "sqrt"] }
255
+ }
256
+ }
257
+ }
258
+ },
259
+ "instructionSubsection": {
260
+ "type": "object",
261
+ "required": ["subsection", "items"],
262
+ "properties": {
263
+ "subsection": { "type": "string" },
264
+ "items": {
265
+ "type": "array",
266
+ "items": {
267
+ "anyOf": [
268
+ { "type": "string" },
269
+ { "$ref": "#/definitions/instruction" }
270
+ ]
271
+ }
272
+ }
273
+ }
274
+ },
275
+ "storage": {
276
+ "type": "object",
277
+ "properties": {
278
+ "roomTemp": { "$ref": "#/definitions/storageMethod" },
279
+ "refrigerated": { "$ref": "#/definitions/storageMethod" },
280
+ "frozen": {
281
+ "allOf": [
282
+ { "$ref": "#/definitions/storageMethod" },
283
+ {
284
+ "type": "object",
285
+ "properties": { "thawing": { "type": "string" } }
286
+ }
287
+ ]
288
+ },
289
+ "reheating": { "type": "string" },
290
+ "makeAhead": {
291
+ "type": "array",
292
+ "items": {
293
+ "allOf": [
294
+ { "$ref": "#/definitions/storageMethod" },
295
+ {
296
+ "type": "object",
297
+ "required": ["component", "storage"],
298
+ "properties": {
299
+ "component": { "type": "string" },
300
+ "storage": { "type": "string", "enum": ["roomTemp", "refrigerated", "frozen"] }
301
+ }
302
+ }
303
+ ]
304
+ }
305
+ }
306
+ }
307
+ },
308
+ "storageMethod": {
309
+ "type": "object",
310
+ "required": ["duration"],
311
+ "properties": {
312
+ "duration": { "type": "string", "pattern": "^P" },
313
+ "method": { "type": "string" },
314
+ "notes": { "type": "string" }
315
+ }
316
+ },
317
+ "substitution": {
318
+ "type": "object",
319
+ "required": ["ingredient"],
320
+ "properties": {
321
+ "ingredient": { "type": "string" },
322
+ "critical": { "type": "boolean" },
323
+ "notes": { "type": "string" },
324
+ "alternatives": {
325
+ "type": "array",
326
+ "items": {
327
+ "type": "object",
328
+ "required": ["name", "ratio"],
329
+ "properties": {
330
+ "name": { "type": "string" },
331
+ "ratio": { "type": "string" },
332
+ "notes": { "type": "string" },
333
+ "impact": { "type": "string" },
334
+ "dietary": {
335
+ "type": "array",
336
+ "items": { "type": "string" }
337
+ }
338
+ }
339
+ }
340
+ }
341
+ }
342
+ }
343
+ }
344
+ }