soustack 0.3.0 → 0.4.1

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.
Files changed (148) hide show
  1. package/README.md +44 -27
  2. package/dist/cli/index.js +5225 -992
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/index.d.mts +163 -91
  5. package/dist/index.d.ts +163 -91
  6. package/dist/index.js +5077 -1007
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +5076 -1007
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/{scrape.d.mts → scrape/index.d.mts} +88 -74
  11. package/dist/{scrape.d.ts → scrape/index.d.ts} +88 -74
  12. package/dist/{scrape.js → scrape/index.js} +255 -124
  13. package/dist/scrape/index.js.map +1 -0
  14. package/dist/{scrape.mjs → scrape/index.mjs} +255 -124
  15. package/dist/scrape/index.mjs.map +1 -0
  16. package/package.json +21 -9
  17. package/spec/.sync-meta.json +149 -0
  18. package/spec/SOUSTACK_SPEC_VERSION +1 -0
  19. package/spec/defs/common.schema.json +46 -0
  20. package/spec/defs/duration.schema.json +33 -0
  21. package/spec/defs/entities.schema.json +111 -0
  22. package/spec/defs/ingredientQuantified.schema.json +9 -0
  23. package/spec/defs/quantity.schema.json +16 -0
  24. package/spec/defs/scalingRule.schema.json +127 -0
  25. package/spec/defs/temperature.schema.json +63 -0
  26. package/spec/fixtures/content/illustrated-step.valid.json +24 -0
  27. package/spec/fixtures/invalid/equipment-unknown-reference.invalid.json +38 -0
  28. package/spec/fixtures/invalid/mise-en-place-unknown-equipment.invalid.json +37 -0
  29. package/spec/fixtures/invalid/mise-en-place-unknown-input.invalid.json +41 -0
  30. package/spec/fixtures/invalid/storage-leftovers-missing-method.invalid.json +31 -0
  31. package/spec/fixtures/invalid/storage-leftovers-wrong-type.invalid.json +23 -0
  32. package/spec/fixtures/level/base-full.valid.json +162 -0
  33. package/spec/fixtures/level/base-missing-yield.invalid.json +12 -0
  34. package/spec/fixtures/level/lite-min.valid.json +14 -0
  35. package/spec/fixtures/profile/profile-base.valid.json +20 -0
  36. package/spec/fixtures/profile/profile-equipped.valid.json +28 -0
  37. package/spec/fixtures/profile/profile-illustrated.valid.json +28 -0
  38. package/spec/fixtures/profile/profile-lite.valid.json +13 -0
  39. package/spec/fixtures/profile/profile-prepped.valid.json +31 -0
  40. package/spec/fixtures/profile/profile-scalable-missing-scaling.invalid.json +29 -0
  41. package/spec/fixtures/profile/profile-scalable.valid.json +49 -0
  42. package/spec/fixtures/profile/profile-timed-missing-structured.invalid.json +30 -0
  43. package/spec/fixtures/scaling/bakers-percent-missing-ref.invalid.json +41 -0
  44. package/spec/fixtures/scaling/bakers-percent.valid.json +51 -0
  45. package/spec/fixtures/scaling/discrete-range.invalid.json +36 -0
  46. package/spec/fixtures/scaling/missing-quantified.invalid.json +40 -0
  47. package/spec/fixtures/scaling/reject-bakersPercentage.invalid.json +50 -0
  48. package/spec/fixtures/stacks/compute-missing-timed.invalid.json +32 -0
  49. package/spec/fixtures/stacks/dietary-no-signal.invalid.json +16 -0
  50. package/spec/fixtures/stacks/illustrated-empty.invalid.json +13 -0
  51. package/spec/fixtures/stacks/quantified-string.invalid.json +22 -0
  52. package/spec/fixtures/stacks/referenced-missing-input.invalid.json +32 -0
  53. package/spec/fixtures/stacks/storage-min.valid.json +20 -0
  54. package/spec/fixtures/stacks/storage-no-duration.invalid.json +16 -0
  55. package/spec/fixtures/stacks/timed-implies-structured.valid.json +50 -0
  56. package/spec/fixtures/stacks/timed-range.invalid.json +33 -0
  57. package/spec/fixtures/valid/equipment-scaling-rules.valid.json +76 -0
  58. package/spec/fixtures/valid/equipment-strings.valid.json +31 -0
  59. package/spec/fixtures/valid/equipment-structured-uses.valid.json +47 -0
  60. package/spec/fixtures/valid/mise-en-place-basic.valid.json +31 -0
  61. package/spec/fixtures/valid/mise-en-place-referenced-equipment.valid.json +51 -0
  62. package/spec/fixtures/valid/prep-ingredient-strings.valid.json +48 -0
  63. package/spec/fixtures/valid/prep-ingredient-structured.valid.json +45 -0
  64. package/spec/fixtures/valid/profile-equipped.valid.json +29 -0
  65. package/spec/fixtures/valid/profile-prepped.valid.json +32 -0
  66. package/spec/fixtures/valid/quantified-nested-ingredient-sections.valid.json +61 -0
  67. package/spec/fixtures/valid/referenced-scaling.valid.json +67 -0
  68. package/spec/fixtures/valid/storage-leftovers-simple.valid.json +27 -0
  69. package/spec/fixtures/valid/storage-leftovers-structured.valid.json +43 -0
  70. package/spec/fixtures/valid/structured-nested-step-sections.valid.json +84 -0
  71. package/spec/schemas/stacks-registry.schema.json +108 -0
  72. package/spec/soustack.schema.json +2379 -0
  73. package/spec/stacks/compute.schema.json +7 -0
  74. package/spec/stacks/compute@1.md +22 -0
  75. package/spec/stacks/dietary.schema.json +45 -0
  76. package/spec/stacks/dietary@1.md +24 -0
  77. package/spec/stacks/equipment.schema.json +98 -0
  78. package/spec/stacks/equipment@1.md +244 -0
  79. package/spec/stacks/illustrated.schema.json +54 -0
  80. package/spec/stacks/illustrated@1.md +24 -0
  81. package/spec/stacks/prep.schema.json +76 -0
  82. package/spec/stacks/prep@1.md +276 -0
  83. package/spec/stacks/quantified.schema.json +74 -0
  84. package/spec/stacks/quantified@1.md +24 -0
  85. package/spec/stacks/referenced.schema.json +96 -0
  86. package/spec/stacks/referenced@1.md +23 -0
  87. package/spec/stacks/registry.json +112 -0
  88. package/spec/stacks/scaling.schema.json +99 -0
  89. package/spec/stacks/scaling@1.md +238 -0
  90. package/spec/stacks/storage.schema.json +132 -0
  91. package/spec/stacks/storage@1.md +256 -0
  92. package/spec/stacks/structured.schema.json +48 -0
  93. package/spec/stacks/structured@1.md +24 -0
  94. package/spec/stacks/substitutions.schema.json +43 -0
  95. package/spec/stacks/substitutions@1.md +24 -0
  96. package/spec/stacks/techniques.schema.json +28 -0
  97. package/spec/stacks/techniques@1.md +23 -0
  98. package/spec/stacks/timed.schema.json +60 -0
  99. package/spec/stacks/timed@1.md +23 -0
  100. package/src/defs/common.schema.json +46 -0
  101. package/src/defs/duration.schema.json +33 -0
  102. package/src/defs/entities.schema.json +111 -0
  103. package/src/defs/ingredientQuantified.schema.json +9 -0
  104. package/src/defs/quantity.schema.json +16 -0
  105. package/src/defs/scalingRule.schema.json +127 -0
  106. package/src/defs/temperature.schema.json +63 -0
  107. package/src/profiles/base.schema.json +2 -2
  108. package/src/profiles/equipped.schema.json +10 -0
  109. package/src/profiles/illustrated.schema.json +4 -4
  110. package/src/profiles/lite.schema.json +10 -0
  111. package/src/profiles/prepped.schema.json +10 -0
  112. package/src/profiles/scalable.schema.json +6 -6
  113. package/src/profiles/timed.schema.json +10 -0
  114. package/src/schema.json +2271 -248
  115. package/src/schemas/stacks-registry.schema.json +108 -0
  116. package/src/soustack.schema.json +2271 -248
  117. package/src/stacks/compute.schema.json +7 -0
  118. package/src/stacks/compute@1.md +22 -0
  119. package/src/stacks/dietary.schema.json +45 -0
  120. package/src/stacks/dietary@1.md +24 -0
  121. package/src/stacks/equipment.schema.json +98 -0
  122. package/src/stacks/equipment@1.md +244 -0
  123. package/src/stacks/illustrated.schema.json +54 -0
  124. package/src/stacks/illustrated@1.md +24 -0
  125. package/src/stacks/prep.schema.json +76 -0
  126. package/src/stacks/prep@1.md +276 -0
  127. package/src/stacks/quantified.schema.json +74 -0
  128. package/src/stacks/quantified@1.md +24 -0
  129. package/src/stacks/referenced.schema.json +96 -0
  130. package/src/stacks/referenced@1.md +23 -0
  131. package/src/stacks/registry.json +112 -0
  132. package/src/stacks/scaling.schema.json +99 -0
  133. package/src/stacks/scaling@1.md +238 -0
  134. package/src/stacks/storage.schema.json +132 -0
  135. package/src/stacks/storage@1.md +256 -0
  136. package/src/stacks/structured.schema.json +48 -0
  137. package/src/stacks/structured@1.md +24 -0
  138. package/src/stacks/substitutions.schema.json +43 -0
  139. package/src/stacks/substitutions@1.md +24 -0
  140. package/src/stacks/techniques.schema.json +28 -0
  141. package/src/stacks/techniques@1.md +23 -0
  142. package/src/stacks/timed.schema.json +60 -0
  143. package/src/stacks/timed@1.md +23 -0
  144. package/dist/scrape.js.map +0 -1
  145. package/dist/scrape.mjs.map +0 -1
  146. package/src/profiles/cookable.schema.json +0 -18
  147. package/src/profiles/quantified.schema.json +0 -43
  148. package/src/profiles/schedulable.schema.json +0 -43
@@ -0,0 +1,99 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://soustack.spec/stacks/scaling.schema.json",
4
+ "title": "Scaling Stack",
5
+ "type": "object",
6
+ "properties": {
7
+ "ingredients": {
8
+ "type": "array",
9
+ "items": {
10
+ "anyOf": [
11
+ { "$ref": "#/$defs/ingredient" },
12
+ { "$ref": "#/$defs/ingredientSection" }
13
+ ]
14
+ }
15
+ },
16
+ "scaling": {
17
+ "type": "object",
18
+ "properties": {
19
+ "discrete": {
20
+ "type": "object",
21
+ "properties": {
22
+ "min": { "type": "integer", "minimum": 1 },
23
+ "max": { "type": "integer", "minimum": 1 },
24
+ "step": { "type": "integer", "minimum": 1 },
25
+ "metadata": { "type": "object", "additionalProperties": true }
26
+ },
27
+ "required": ["min", "max"],
28
+ "additionalProperties": false,
29
+ "patternProperties": {
30
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
31
+ }
32
+ },
33
+ "metadata": { "type": "object", "additionalProperties": true }
34
+ },
35
+ "required": ["discrete"],
36
+ "additionalProperties": false,
37
+ "patternProperties": {
38
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
39
+ }
40
+ }
41
+ },
42
+ "required": ["ingredients", "scaling"],
43
+ "$defs": {
44
+ "ingredient": {
45
+ "type": "object",
46
+ "properties": {
47
+ "id": { "type": "string" },
48
+ "name": { "type": "string" },
49
+ "quantity": { "$ref": "../defs/quantity.schema.json" },
50
+ "temperature": { "$ref": "../defs/temperature.schema.json" },
51
+ "notes": { "type": "string" },
52
+ "prep": {
53
+ "oneOf": [
54
+ { "type": "string", "minLength": 1 },
55
+ {
56
+ "type": "array",
57
+ "minItems": 1,
58
+ "items": {
59
+ "anyOf": [
60
+ { "type": "string", "minLength": 1 },
61
+ { "$ref": "../stacks/prep.schema.json#/$defs/prepItem" }
62
+ ]
63
+ }
64
+ }
65
+ ]
66
+ },
67
+ "metadata": { "type": "object", "additionalProperties": true },
68
+ "scaling": { "$ref": "../defs/scalingRule.schema.json" }
69
+ },
70
+ "required": ["id", "name", "quantity"],
71
+ "additionalProperties": false,
72
+ "patternProperties": {
73
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
74
+ }
75
+ },
76
+ "ingredientSection": {
77
+ "type": "object",
78
+ "properties": {
79
+ "section": { "type": "string" },
80
+ "ingredients": {
81
+ "type": "array",
82
+ "items": {
83
+ "anyOf": [
84
+ { "$ref": "#/$defs/ingredient" },
85
+ { "$ref": "#/$defs/ingredientSection" }
86
+ ]
87
+ }
88
+ },
89
+ "metadata": { "type": "object", "additionalProperties": true }
90
+ },
91
+ "required": ["section", "ingredients"],
92
+ "additionalProperties": false,
93
+ "patternProperties": {
94
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
95
+ }
96
+ }
97
+ },
98
+ "additionalProperties": false
99
+ }
@@ -0,0 +1,238 @@
1
+ #scaling@1
2
+
3
+ ---
4
+
5
+ ## Purpose
6
+
7
+ The `scaling@1` stack standardizes how ingredient quantities change when a recipe is scaled, so independent implementations produce consistent results.
8
+
9
+ This stack is intended to support the **Scalable** profile.
10
+
11
+ ---
12
+
13
+ ## Requirements
14
+
15
+ A document that declares `scaling@1`:
16
+
17
+ • MUST declare `quantified@1`.
18
+ • SHOULD declare `structured@1` (recommended for stable ingredient IDs).
19
+ • MUST satisfy all structural rules enabled by this stack.
20
+ • MUST satisfy the semantic rules described below.
21
+
22
+ ---
23
+
24
+ ## Data Model
25
+
26
+ When `scaling@1` is declared, quantified ingredients MAY include an optional `scaling` field.
27
+
28
+ Field
29
+ ingredient.scaling : ScalingRule
30
+
31
+ ScalingRule is defined in `defs/scalingRule.schema.json` and is a closed object with a required `mode`.
32
+
33
+ ---
34
+
35
+ ## Scaling Modes
36
+
37
+ ### linear
38
+
39
+ Multiply the ingredient quantity by the scale factor.
40
+
41
+ mode = "linear"
42
+
43
+ If `scaling` is omitted, implementations MUST treat the ingredient as linear by default.
44
+
45
+ ---
46
+
47
+ ### fixed
48
+
49
+ Do not scale this ingredient quantity.
50
+
51
+ Examples:
52
+ • 1 bay leaf
53
+ • 1 cinnamon stick
54
+ • 1 pan
55
+
56
+ mode = "fixed"
57
+
58
+ ---
59
+
60
+ ### discrete
61
+
62
+ Scale using discrete increments (for example, whole eggs), with optional rounding and bounds.
63
+
64
+ Fields
65
+ • mode = "discrete"
66
+ • step (optional, > 0): discrete increment size
67
+ • rounding (optional): nearest | ceil | floor
68
+ • min (optional): minimum allowed result
69
+ • max (optional): maximum allowed result
70
+
71
+ Semantic defaults
72
+ • If step is not present, default is 1
73
+ • If rounding is not present, default is nearest
74
+
75
+ ---
76
+
77
+ ### toTaste
78
+
79
+ Quantity is informational and MUST NOT be mechanically scaled.
80
+
81
+ Examples:
82
+ • salt to taste
83
+ • pepper to taste
84
+
85
+ mode = "toTaste"
86
+
87
+ ---
88
+
89
+ ### bakersPercent
90
+
91
+ Represents the ingredient quantity as a baker’s percentage of a base ingredient (typically flour).
92
+
93
+ Fields
94
+ • mode = "bakersPercent"
95
+ • percent (> 0): percentage value (for example, 70 for 70%)
96
+ • of (string): ingredient id of the base ingredient
97
+
98
+ ---
99
+
100
+ ## Scale Factor
101
+
102
+ Scaling is applied using a scale factor F.
103
+
104
+ How F is selected depends on the consumer implementation:
105
+
106
+ • Some consumers may accept an explicit factor (for example, “2x”).
107
+ • Some consumers may compute F from yield changes (for example, “scale from 4 servings to 6 servings”).
108
+
109
+ This stack standardizes how ingredient rules respond to F, not how F is chosen.
110
+
111
+ ---
112
+
113
+ ## Semantic Validation Rules (Normative)
114
+
115
+ Validators MUST enforce the following rules:
116
+
117
+ 1. Prerequisite
118
+ If `scaling@1` is declared, `quantified@1` MUST also be declared.
119
+
120
+ 2. Baker’s percent reference
121
+ For mode = "bakersPercent", the `of` field MUST resolve to an existing ingredient id.
122
+
123
+ 3. Discrete sanity
124
+ For mode = "discrete", if both `min` and `max` are present, min MUST be less than or equal to max.
125
+
126
+ 4. Closed rule objects
127
+ ScalingRule MUST NOT contain unspecified fields. This is enforced by schema.
128
+
129
+ Consumers MUST apply the following semantic defaults:
130
+
131
+ • Missing `scaling` ⇒ treat as linear
132
+ • discrete.step default = 1
133
+ • discrete.rounding default = nearest
134
+
135
+ ---
136
+
137
+ ## Scaling Behavior (Normative)
138
+
139
+ Given an ingredient with base quantity amount A and scale factor F:
140
+
141
+ • linear
142
+ scaled amount = A × F
143
+
144
+ • fixed
145
+ scaled amount = A
146
+
147
+ • toTaste
148
+ scaled amount = A (informational only)
149
+ Consumers MAY hide numeric scaling UI for this item.
150
+
151
+ • discrete
152
+
153
+ 1. raw = A × F
154
+ 2. if step is present, units = raw ÷ step
155
+ 3. apply rounding to units
156
+ 4. scaled = units × step
157
+ 5. clamp to min / max if present
158
+
159
+ • bakersPercent
160
+
161
+ 1. let B be the amount of the ingredient referenced by `of`
162
+ 2. scaled amount = B × (percent ÷ 100)
163
+
164
+ ---
165
+
166
+ ## Examples
167
+
168
+ Linear (default)
169
+
170
+ Ingredient
171
+ id: i_sugar
172
+ name: Sugar
173
+ quantity: 100 g
174
+ (no scaling field → treated as linear)
175
+
176
+ ---
177
+
178
+ Fixed
179
+
180
+ Ingredient
181
+ id: i_bayleaf
182
+ name: Bay leaf
183
+ quantity: 1 leaf
184
+ scaling: mode = fixed
185
+
186
+ ---
187
+
188
+ Discrete (eggs)
189
+
190
+ Ingredient
191
+ id: i_eggs
192
+ name: Eggs
193
+ quantity: 2 egg
194
+ scaling: mode = discrete, rounding = ceil
195
+
196
+ ---
197
+
198
+ To taste
199
+
200
+ Ingredient
201
+ id: i_salt
202
+ name: Salt
203
+ quantity: 1 tsp
204
+ scaling: mode = toTaste
205
+
206
+ ---
207
+
208
+ Baker’s percent
209
+
210
+ Ingredient A
211
+ id: i_flour
212
+ name: Flour
213
+ quantity: 500 g
214
+
215
+ Ingredient B
216
+ id: i_water
217
+ name: Water
218
+ quantity: 350 g
219
+ scaling: mode = bakersPercent, percent = 70, of = i_flour
220
+
221
+ ---
222
+
223
+ ## Fixtures
224
+
225
+ Implementations SHOULD include fixtures covering:
226
+
227
+ Valid cases
228
+ • linear default (no scaling rule)
229
+ • discrete with rounding
230
+ • fixed
231
+ • toTaste
232
+ • bakersPercent with resolvable `of`
233
+
234
+ Invalid cases
235
+ • scaling declared without quantified
236
+ • bakersPercent with missing or invalid `of`
237
+ • invalid rounding value
238
+ • discrete with min greater than max
@@ -0,0 +1,132 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://soustack.spec/stacks/storage.schema.json",
4
+ "title": "Storage Stack",
5
+ "type": "object",
6
+ "properties": {
7
+ "storage": {
8
+ "type": "object",
9
+ "properties": {
10
+ "roomTemp": { "$ref": "#/$defs/storageMethod" },
11
+ "refrigerated": { "$ref": "#/$defs/storageMethod" },
12
+ "frozen": { "$ref": "#/$defs/storageMethod" },
13
+ "leftovers": { "$ref": "#/$defs/leftovers" },
14
+ "metadata": { "type": "object", "additionalProperties": true }
15
+ },
16
+ "anyOf": [
17
+ { "required": ["roomTemp"] },
18
+ { "required": ["refrigerated"] },
19
+ { "required": ["frozen"] }
20
+ ],
21
+ "additionalProperties": false,
22
+ "patternProperties": {
23
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
24
+ }
25
+ }
26
+ },
27
+ "required": ["storage"],
28
+ "$defs": {
29
+ "storageMethod": {
30
+ "type": "object",
31
+ "properties": {
32
+ "duration": { "$ref": "../defs/duration.schema.json#/properties/StorageDuration" },
33
+ "notes": { "type": "string" },
34
+ "metadata": { "type": "object", "additionalProperties": true }
35
+ },
36
+ "required": ["duration"],
37
+ "additionalProperties": false,
38
+ "patternProperties": {
39
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
40
+ }
41
+ },
42
+ "reheatDuration": {
43
+ "type": "object",
44
+ "properties": {
45
+ "minMinutes": { "type": "integer", "minimum": 0 },
46
+ "maxMinutes": { "type": "integer", "minimum": 0 }
47
+ },
48
+ "anyOf": [
49
+ { "required": ["minMinutes"] },
50
+ { "required": ["maxMinutes"] }
51
+ ],
52
+ "additionalProperties": false,
53
+ "patternProperties": {
54
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
55
+ }
56
+ },
57
+ "reheatInstruction": {
58
+ "type": "object",
59
+ "properties": {
60
+ "method": { "type": "string", "minLength": 1 },
61
+ "temp": {
62
+ "type": "object",
63
+ "properties": {
64
+ "value": { "type": "number" },
65
+ "unit": { "type": "string", "enum": ["F", "C"] }
66
+ },
67
+ "required": ["value", "unit"],
68
+ "additionalProperties": false,
69
+ "patternProperties": {
70
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
71
+ }
72
+ },
73
+ "duration": { "$ref": "#/$defs/reheatDuration" },
74
+ "notes": { "type": "string" }
75
+ },
76
+ "required": ["method"],
77
+ "additionalProperties": false,
78
+ "patternProperties": {
79
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
80
+ }
81
+ },
82
+ "portioning": {
83
+ "type": "object",
84
+ "properties": {
85
+ "notes": { "type": "string" },
86
+ "recommendedPortion": {
87
+ "type": "object",
88
+ "properties": {
89
+ "quantity": { "type": "number" },
90
+ "unit": { "type": "string" }
91
+ },
92
+ "required": ["quantity", "unit"],
93
+ "additionalProperties": false,
94
+ "patternProperties": {
95
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
96
+ }
97
+ }
98
+ },
99
+ "required": ["notes"],
100
+ "additionalProperties": false,
101
+ "patternProperties": {
102
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
103
+ }
104
+ },
105
+ "leftovers": {
106
+ "type": "object",
107
+ "properties": {
108
+ "notes": { "type": "string" },
109
+ "reheat": {
110
+ "oneOf": [
111
+ {
112
+ "type": "array",
113
+ "items": { "type": "string" },
114
+ "minItems": 1
115
+ },
116
+ {
117
+ "type": "array",
118
+ "items": { "$ref": "#/$defs/reheatInstruction" },
119
+ "minItems": 1
120
+ }
121
+ ]
122
+ },
123
+ "portioning": { "$ref": "#/$defs/portioning" }
124
+ },
125
+ "additionalProperties": false,
126
+ "patternProperties": {
127
+ "^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
128
+ }
129
+ }
130
+ },
131
+ "additionalProperties": false
132
+ }