soustack 0.2.1 → 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.2",
4
- "title": "Soustack Recipe Schema v0.2",
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"
@@ -51,6 +69,11 @@
51
69
  "type": "string",
52
70
  "format": "date-time"
53
71
  },
72
+ "metadata": {
73
+ "type": "object",
74
+ "additionalProperties": true,
75
+ "description": "Free-form vendor metadata"
76
+ },
54
77
  "source": {
55
78
  "type": "object",
56
79
  "properties": {
@@ -110,24 +133,16 @@
110
133
  }
111
134
  },
112
135
  "time": {
113
- "oneOf": [
114
- {
115
- "type": "object",
116
- "properties": {
117
- "prep": { "type": "number" },
118
- "active": { "type": "number" },
119
- "passive": { "type": "number" },
120
- "total": { "type": "number" }
121
- }
122
- },
123
- {
124
- "type": "object",
125
- "properties": {
126
- "prepTime": { "type": "string" },
127
- "cookTime": { "type": "string" }
128
- }
129
- }
130
- ]
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
131
146
  },
132
147
  "quantity": {
133
148
  "type": "object",
@@ -228,7 +243,13 @@
228
243
  "type": "object",
229
244
  "required": ["duration", "type"],
230
245
  "properties": {
231
- "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
+ },
232
253
  "type": { "type": "string", "enum": ["active", "passive"] },
233
254
  "scaling": { "type": "string", "enum": ["linear", "fixed", "sqrt"] }
234
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
+ }