soustack 0.4.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.
- package/README.md +4 -4
- package/dist/cli/index.js +4412 -1275
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +106 -80
- package/dist/index.d.ts +106 -80
- package/dist/index.js +4527 -1360
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4527 -1360
- package/dist/index.mjs.map +1 -1
- package/dist/scrape/index.d.mts +86 -74
- package/dist/scrape/index.d.ts +86 -74
- package/dist/scrape/index.js +91 -64
- package/dist/scrape/index.js.map +1 -1
- package/dist/scrape/index.mjs +91 -64
- package/dist/scrape/index.mjs.map +1 -1
- package/package.json +15 -6
- package/spec/.sync-meta.json +149 -0
- package/spec/SOUSTACK_SPEC_VERSION +1 -0
- package/spec/defs/common.schema.json +46 -0
- package/spec/defs/duration.schema.json +33 -0
- package/spec/defs/entities.schema.json +111 -0
- package/spec/defs/ingredientQuantified.schema.json +9 -0
- package/spec/defs/quantity.schema.json +16 -0
- package/spec/defs/scalingRule.schema.json +127 -0
- package/spec/defs/temperature.schema.json +63 -0
- package/spec/fixtures/content/illustrated-step.valid.json +24 -0
- package/spec/fixtures/invalid/equipment-unknown-reference.invalid.json +38 -0
- package/spec/fixtures/invalid/mise-en-place-unknown-equipment.invalid.json +37 -0
- package/spec/fixtures/invalid/mise-en-place-unknown-input.invalid.json +41 -0
- package/spec/fixtures/invalid/storage-leftovers-missing-method.invalid.json +31 -0
- package/spec/fixtures/invalid/storage-leftovers-wrong-type.invalid.json +23 -0
- package/spec/fixtures/level/base-full.valid.json +162 -0
- package/spec/fixtures/level/base-missing-yield.invalid.json +12 -0
- package/spec/fixtures/level/lite-min.valid.json +14 -0
- package/spec/fixtures/profile/profile-base.valid.json +20 -0
- package/spec/fixtures/profile/profile-equipped.valid.json +28 -0
- package/spec/fixtures/profile/profile-illustrated.valid.json +28 -0
- package/spec/fixtures/profile/profile-lite.valid.json +13 -0
- package/spec/fixtures/profile/profile-prepped.valid.json +31 -0
- package/spec/fixtures/profile/profile-scalable-missing-scaling.invalid.json +29 -0
- package/spec/fixtures/profile/profile-scalable.valid.json +49 -0
- package/spec/fixtures/profile/profile-timed-missing-structured.invalid.json +30 -0
- package/spec/fixtures/scaling/bakers-percent-missing-ref.invalid.json +41 -0
- package/spec/fixtures/scaling/bakers-percent.valid.json +51 -0
- package/spec/fixtures/scaling/discrete-range.invalid.json +36 -0
- package/spec/fixtures/scaling/missing-quantified.invalid.json +40 -0
- package/spec/fixtures/scaling/reject-bakersPercentage.invalid.json +50 -0
- package/spec/fixtures/stacks/compute-missing-timed.invalid.json +32 -0
- package/spec/fixtures/stacks/dietary-no-signal.invalid.json +16 -0
- package/spec/fixtures/stacks/illustrated-empty.invalid.json +13 -0
- package/spec/fixtures/stacks/quantified-string.invalid.json +22 -0
- package/spec/fixtures/stacks/referenced-missing-input.invalid.json +32 -0
- package/spec/fixtures/stacks/storage-min.valid.json +20 -0
- package/spec/fixtures/stacks/storage-no-duration.invalid.json +16 -0
- package/spec/fixtures/stacks/timed-implies-structured.valid.json +50 -0
- package/spec/fixtures/stacks/timed-range.invalid.json +33 -0
- package/spec/fixtures/valid/equipment-scaling-rules.valid.json +76 -0
- package/spec/fixtures/valid/equipment-strings.valid.json +31 -0
- package/spec/fixtures/valid/equipment-structured-uses.valid.json +47 -0
- package/spec/fixtures/valid/mise-en-place-basic.valid.json +31 -0
- package/spec/fixtures/valid/mise-en-place-referenced-equipment.valid.json +51 -0
- package/spec/fixtures/valid/prep-ingredient-strings.valid.json +48 -0
- package/spec/fixtures/valid/prep-ingredient-structured.valid.json +45 -0
- package/spec/fixtures/valid/profile-equipped.valid.json +29 -0
- package/spec/fixtures/valid/profile-prepped.valid.json +32 -0
- package/spec/fixtures/valid/quantified-nested-ingredient-sections.valid.json +61 -0
- package/spec/fixtures/valid/referenced-scaling.valid.json +67 -0
- package/spec/fixtures/valid/storage-leftovers-simple.valid.json +27 -0
- package/spec/fixtures/valid/storage-leftovers-structured.valid.json +43 -0
- package/spec/fixtures/valid/structured-nested-step-sections.valid.json +84 -0
- package/spec/schemas/stacks-registry.schema.json +108 -0
- package/spec/soustack.schema.json +2379 -0
- package/spec/stacks/compute.schema.json +7 -0
- package/spec/stacks/compute@1.md +22 -0
- package/spec/stacks/dietary.schema.json +45 -0
- package/spec/stacks/dietary@1.md +24 -0
- package/spec/stacks/equipment.schema.json +98 -0
- package/spec/stacks/equipment@1.md +244 -0
- package/spec/stacks/illustrated.schema.json +54 -0
- package/spec/stacks/illustrated@1.md +24 -0
- package/spec/stacks/prep.schema.json +76 -0
- package/spec/stacks/prep@1.md +276 -0
- package/spec/stacks/quantified.schema.json +74 -0
- package/spec/stacks/quantified@1.md +24 -0
- package/spec/stacks/referenced.schema.json +96 -0
- package/spec/stacks/referenced@1.md +23 -0
- package/spec/stacks/registry.json +112 -0
- package/spec/stacks/scaling.schema.json +99 -0
- package/spec/stacks/scaling@1.md +238 -0
- package/spec/stacks/storage.schema.json +132 -0
- package/spec/stacks/storage@1.md +256 -0
- package/spec/stacks/structured.schema.json +48 -0
- package/spec/stacks/structured@1.md +24 -0
- package/spec/stacks/substitutions.schema.json +43 -0
- package/spec/stacks/substitutions@1.md +24 -0
- package/spec/stacks/techniques.schema.json +28 -0
- package/spec/stacks/techniques@1.md +23 -0
- package/spec/stacks/timed.schema.json +60 -0
- package/spec/stacks/timed@1.md +23 -0
- package/src/defs/common.schema.json +46 -0
- package/src/defs/duration.schema.json +33 -0
- package/src/defs/entities.schema.json +111 -0
- package/src/defs/ingredientQuantified.schema.json +9 -0
- package/src/defs/quantity.schema.json +16 -0
- package/src/defs/scalingRule.schema.json +127 -0
- package/src/defs/temperature.schema.json +63 -0
- package/src/profiles/base.schema.json +2 -2
- package/src/profiles/equipped.schema.json +10 -0
- package/src/profiles/illustrated.schema.json +4 -4
- package/src/profiles/lite.schema.json +10 -0
- package/src/profiles/prepped.schema.json +10 -0
- package/src/profiles/scalable.schema.json +6 -6
- package/src/profiles/timed.schema.json +10 -0
- package/src/schema.json +2271 -248
- package/src/schemas/stacks-registry.schema.json +108 -0
- package/src/soustack.schema.json +2271 -248
- package/src/stacks/compute.schema.json +7 -0
- package/src/stacks/compute@1.md +22 -0
- package/src/stacks/dietary.schema.json +45 -0
- package/src/stacks/dietary@1.md +24 -0
- package/src/stacks/equipment.schema.json +98 -0
- package/src/stacks/equipment@1.md +244 -0
- package/src/stacks/illustrated.schema.json +54 -0
- package/src/stacks/illustrated@1.md +24 -0
- package/src/stacks/prep.schema.json +76 -0
- package/src/stacks/prep@1.md +276 -0
- package/src/stacks/quantified.schema.json +74 -0
- package/src/stacks/quantified@1.md +24 -0
- package/src/stacks/referenced.schema.json +96 -0
- package/src/stacks/referenced@1.md +23 -0
- package/src/stacks/registry.json +112 -0
- package/src/stacks/scaling.schema.json +99 -0
- package/src/stacks/scaling@1.md +238 -0
- package/src/stacks/storage.schema.json +132 -0
- package/src/stacks/storage@1.md +256 -0
- package/src/stacks/structured.schema.json +48 -0
- package/src/stacks/structured@1.md +24 -0
- package/src/stacks/substitutions.schema.json +43 -0
- package/src/stacks/substitutions@1.md +24 -0
- package/src/stacks/techniques.schema.json +28 -0
- package/src/stacks/techniques@1.md +23 -0
- package/src/stacks/timed.schema.json +60 -0
- package/src/stacks/timed@1.md +23 -0
- package/src/profiles/cookable.schema.json +0 -18
- package/src/profiles/quantified.schema.json +0 -43
- package/src/profiles/schedulable.schema.json +0 -43
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#storage@1
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
The `storage@1` stack enables recipes to declare storage conditions and duration for prepared dishes, with optional leftovers and reheating guidance.
|
|
8
|
+
|
|
9
|
+
This stack is adoption-first: storage can be declared with minimal information, or extended with detailed leftovers and reheating instructions.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
A document that declares `storage@1`:
|
|
16
|
+
|
|
17
|
+
• MUST satisfy all structural rules enabled by this stack.
|
|
18
|
+
• MUST satisfy the semantic rules described below.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Data Model
|
|
23
|
+
|
|
24
|
+
When `storage@1` is declared, the document MUST include a top-level `storage` object.
|
|
25
|
+
|
|
26
|
+
### Storage Object
|
|
27
|
+
|
|
28
|
+
The `storage` object MUST include exactly one of:
|
|
29
|
+
• `roomTemp`: storage at room temperature
|
|
30
|
+
• `refrigerated`: storage in refrigeration
|
|
31
|
+
• `frozen`: storage in freezing conditions
|
|
32
|
+
|
|
33
|
+
Each storage method is a `storageMethod` object with:
|
|
34
|
+
• `duration` (required): ISO 8601 duration string (pattern `^P`)
|
|
35
|
+
• `notes` (optional): string
|
|
36
|
+
• `metadata` (optional): object
|
|
37
|
+
|
|
38
|
+
### Leftovers and Reheating
|
|
39
|
+
|
|
40
|
+
The `storage` object MAY include an optional `leftovers` field for leftovers and reheating guidance.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Leftovers and Reheating
|
|
45
|
+
|
|
46
|
+
The `leftovers` object supports:
|
|
47
|
+
|
|
48
|
+
• `notes` (optional): string with general guidance
|
|
49
|
+
• `reheat` (optional): array of reheating instructions (see below)
|
|
50
|
+
• `portioning` (optional): portion guidance object
|
|
51
|
+
|
|
52
|
+
### Reheat Field
|
|
53
|
+
|
|
54
|
+
The `reheat` field supports two formats:
|
|
55
|
+
|
|
56
|
+
#### Simple (Array of Strings)
|
|
57
|
+
|
|
58
|
+
An array of freeform string instructions:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"storage": {
|
|
63
|
+
"refrigerated": {
|
|
64
|
+
"duration": { "iso8601": "P4D" }
|
|
65
|
+
},
|
|
66
|
+
"leftovers": {
|
|
67
|
+
"notes": "Store in an airtight container.",
|
|
68
|
+
"reheat": [
|
|
69
|
+
"Microwave 2–3 minutes, stirring halfway.",
|
|
70
|
+
"Or warm in a skillet over medium heat with a splash of water."
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### Structured (Array of Objects)
|
|
78
|
+
|
|
79
|
+
An array of structured reheating instruction objects:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"storage": {
|
|
84
|
+
"frozen": {
|
|
85
|
+
"duration": { "iso8601": "P2M" }
|
|
86
|
+
},
|
|
87
|
+
"leftovers": {
|
|
88
|
+
"portioning": { "notes": "Cool completely, then portion into containers." },
|
|
89
|
+
"reheat": [
|
|
90
|
+
{
|
|
91
|
+
"method": "microwave",
|
|
92
|
+
"duration": { "minMinutes": 2, "maxMinutes": 3 },
|
|
93
|
+
"notes": "Stir halfway."
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"method": "oven",
|
|
97
|
+
"temp": { "value": 350, "unit": "F" },
|
|
98
|
+
"notes": "Cover and heat until hot throughout."
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Reheat Instruction Object
|
|
107
|
+
|
|
108
|
+
Each structured reheating instruction includes:
|
|
109
|
+
|
|
110
|
+
• `method` (required): string describing the reheating method (freeform in v1)
|
|
111
|
+
• `temp` (optional): object with `value` (number) and `unit` ("F" or "C")
|
|
112
|
+
• `duration` (optional): object with `minMinutes` and/or `maxMinutes` (integers >= 0)
|
|
113
|
+
• `notes` (optional): string
|
|
114
|
+
|
|
115
|
+
### Reheat Duration
|
|
116
|
+
|
|
117
|
+
The `duration` object supports:
|
|
118
|
+
• `minMinutes` (optional): integer >= 0
|
|
119
|
+
• `maxMinutes` (optional): integer >= 0
|
|
120
|
+
• At least one of `minMinutes` or `maxMinutes` must be present
|
|
121
|
+
|
|
122
|
+
If both are present, `minMinutes` should be <= `maxMinutes` (semantic validation may enforce this in tooling).
|
|
123
|
+
|
|
124
|
+
### Portioning
|
|
125
|
+
|
|
126
|
+
The `portioning` object includes:
|
|
127
|
+
• `notes` (required): string with portioning guidance
|
|
128
|
+
• `recommendedPortion` (optional): object with `quantity` (number) and `unit` (string)
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Semantics
|
|
133
|
+
|
|
134
|
+
### Storage Methods
|
|
135
|
+
|
|
136
|
+
Storage methods (`roomTemp`, `refrigerated`, `frozen`) are mutually exclusive. Exactly one must be present.
|
|
137
|
+
|
|
138
|
+
### Reheat Method
|
|
139
|
+
|
|
140
|
+
The `method` field in structured reheating instructions is freeform in v1. Consumers may map common values (e.g., "microwave", "oven", "stovetop") to display or planning features, but validation does not enforce specific values.
|
|
141
|
+
|
|
142
|
+
### Scaling
|
|
143
|
+
|
|
144
|
+
If no scaling factor exists in the document, leftovers guidance does not change. Leftovers guidance is not scaled by recipe scale factors.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Composition
|
|
149
|
+
|
|
150
|
+
The storage stack is composable with other stacks:
|
|
151
|
+
|
|
152
|
+
• No hard dependency on other stacks
|
|
153
|
+
• Remains monotonic: does not close objects needed by other stacks
|
|
154
|
+
• `leftovers` is optional even when the stack is present
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Semantic Validation Rules (Normative)
|
|
159
|
+
|
|
160
|
+
Validators MUST enforce the following rules:
|
|
161
|
+
|
|
162
|
+
1. Storage method requirement
|
|
163
|
+
The `storage` object MUST include exactly one of `roomTemp`, `refrigerated`, or `frozen`.
|
|
164
|
+
|
|
165
|
+
2. Duration requirement
|
|
166
|
+
Each storage method object MUST include a `duration` field.
|
|
167
|
+
|
|
168
|
+
3. Reheat instruction method requirement
|
|
169
|
+
If `leftovers.reheat` includes structured instruction objects, each object MUST include a `method` field.
|
|
170
|
+
|
|
171
|
+
4. Reheat duration validation
|
|
172
|
+
If a `reheatInstruction.duration` includes both `minMinutes` and `maxMinutes`, `minMinutes` SHOULD be <= `maxMinutes` (tooling may warn but not fail validation).
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Examples
|
|
177
|
+
|
|
178
|
+
Minimal storage
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"storage": {
|
|
183
|
+
"refrigerated": {
|
|
184
|
+
"duration": { "iso8601": "P3D" }
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Storage with simple leftovers
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"storage": {
|
|
195
|
+
"refrigerated": {
|
|
196
|
+
"duration": { "iso8601": "P4D" }
|
|
197
|
+
},
|
|
198
|
+
"leftovers": {
|
|
199
|
+
"notes": "Store in an airtight container.",
|
|
200
|
+
"reheat": [
|
|
201
|
+
"Microwave 2–3 minutes, stirring halfway.",
|
|
202
|
+
"Or warm in a skillet over medium heat with a splash of water."
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Storage with structured reheating
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"storage": {
|
|
214
|
+
"frozen": {
|
|
215
|
+
"duration": { "iso8601": "P2M" }
|
|
216
|
+
},
|
|
217
|
+
"leftovers": {
|
|
218
|
+
"portioning": { "notes": "Cool completely, then portion into containers." },
|
|
219
|
+
"reheat": [
|
|
220
|
+
{
|
|
221
|
+
"method": "microwave",
|
|
222
|
+
"duration": { "minMinutes": 2, "maxMinutes": 3 },
|
|
223
|
+
"notes": "Stir halfway."
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"method": "oven",
|
|
227
|
+
"temp": { "value": 350, "unit": "F" },
|
|
228
|
+
"notes": "Cover and heat until hot throughout."
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Storage with portioning
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"storage": {
|
|
241
|
+
"refrigerated": {
|
|
242
|
+
"duration": { "iso8601": "P5D" }
|
|
243
|
+
},
|
|
244
|
+
"leftovers": {
|
|
245
|
+
"portioning": {
|
|
246
|
+
"notes": "Portion into individual servings before storing.",
|
|
247
|
+
"recommendedPortion": {
|
|
248
|
+
"quantity": 1,
|
|
249
|
+
"unit": "cup"
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://soustack.spec/stacks/structured.schema.json",
|
|
4
|
+
"title": "Structured Stack",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"instructions": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"items": {
|
|
10
|
+
"anyOf": [
|
|
11
|
+
{ "$ref": "#/$defs/step" },
|
|
12
|
+
{ "$ref": "#/$defs/stepSection" }
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"required": ["instructions"],
|
|
18
|
+
"$defs": {
|
|
19
|
+
"step": {
|
|
20
|
+
"allOf": [
|
|
21
|
+
{ "$ref": "../defs/entities.schema.json#/$defs/StepBase" },
|
|
22
|
+
{ "required": ["id"] }
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"stepSection": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"properties": {
|
|
28
|
+
"section": { "type": "string" },
|
|
29
|
+
"steps": {
|
|
30
|
+
"type": "array",
|
|
31
|
+
"items": {
|
|
32
|
+
"anyOf": [
|
|
33
|
+
{ "$ref": "#/$defs/step" },
|
|
34
|
+
{ "$ref": "#/$defs/stepSection" }
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"metadata": { "type": "object", "additionalProperties": true }
|
|
39
|
+
},
|
|
40
|
+
"required": ["section", "steps"],
|
|
41
|
+
"additionalProperties": false,
|
|
42
|
+
"patternProperties": {
|
|
43
|
+
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"additionalProperties": false
|
|
48
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# structured@1
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
The `structured@1` stack requires that instruction steps are structured objects with stable IDs, enabling cross-referencing, timing, and programmatic manipulation.
|
|
5
|
+
|
|
6
|
+
## Adds
|
|
7
|
+
- Top-level `instructions` array with structured step objects containing `id` and `text` fields.
|
|
8
|
+
- Steps may include `dependsOn`, `inputs`, `techniqueIds`, `usesEquipment`, `temperature`, `timing`, `images`, `videos`, and `metadata`.
|
|
9
|
+
- Support for nested step sections.
|
|
10
|
+
|
|
11
|
+
## Requires
|
|
12
|
+
- None
|
|
13
|
+
|
|
14
|
+
## Semantics
|
|
15
|
+
- MUST: Each step object must include `id` and `text` fields.
|
|
16
|
+
- MUST: Step IDs must be unique within the instructions array.
|
|
17
|
+
- NOTE: This stack is a prerequisite for `timed@1` and `referenced@1`.
|
|
18
|
+
|
|
19
|
+
## Composition Notes
|
|
20
|
+
- This stack is monotonic: it adds requirements or fields without removing expressiveness.
|
|
21
|
+
- Interaction: Required by `timed@1` and `referenced@1`. Enables equipment references when combined with `equipment@1`.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://soustack.spec/stacks/substitutions.schema.json",
|
|
4
|
+
"title": "Substitutions Stack",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"substitutions": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"items": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"properties": {
|
|
12
|
+
"for": { "type": "string" },
|
|
13
|
+
"alternatives": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"name": { "type": "string" },
|
|
19
|
+
"ratio": { "type": "string" },
|
|
20
|
+
"metadata": { "type": "object", "additionalProperties": true }
|
|
21
|
+
},
|
|
22
|
+
"required": ["name", "ratio"],
|
|
23
|
+
"additionalProperties": false,
|
|
24
|
+
"patternProperties": {
|
|
25
|
+
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"minItems": 1
|
|
29
|
+
},
|
|
30
|
+
"metadata": { "type": "object", "additionalProperties": true }
|
|
31
|
+
},
|
|
32
|
+
"required": ["for", "alternatives"],
|
|
33
|
+
"additionalProperties": false,
|
|
34
|
+
"patternProperties": {
|
|
35
|
+
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"minItems": 1
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"required": ["substitutions"],
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# substitutions@1
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
The `substitutions@1` stack enables recipes to declare ingredient substitutions with alternatives and ratios.
|
|
5
|
+
|
|
6
|
+
## Adds
|
|
7
|
+
- Top-level `substitutions` array with substitution objects.
|
|
8
|
+
- Each substitution includes `for` (ingredient ID) and `alternatives` array.
|
|
9
|
+
- Each alternative includes `name` and `ratio` fields.
|
|
10
|
+
|
|
11
|
+
## Requires
|
|
12
|
+
- `referenced@1`
|
|
13
|
+
|
|
14
|
+
## Semantics
|
|
15
|
+
- MUST: The `for` field must reference an ingredient ID that exists in the ingredients array.
|
|
16
|
+
- MUST: Each substitution must include at least one alternative.
|
|
17
|
+
- NOTE: This stack requires `referenced@1` to ensure ingredient IDs are available for reference.
|
|
18
|
+
|
|
19
|
+
## Composition Notes
|
|
20
|
+
- This stack is monotonic: it adds requirements or fields without removing expressiveness.
|
|
21
|
+
- Interaction: Requires `referenced@1` for ingredient ID resolution. Substitutions are informational and do not affect recipe structure.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://soustack.spec/stacks/techniques.schema.json",
|
|
4
|
+
"title": "Techniques Stack",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"techniques": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"items": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"properties": {
|
|
12
|
+
"id": { "type": "string" },
|
|
13
|
+
"name": { "type": "string" },
|
|
14
|
+
"description": { "type": "string" },
|
|
15
|
+
"metadata": { "type": "object", "additionalProperties": true }
|
|
16
|
+
},
|
|
17
|
+
"required": ["id", "name"],
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"patternProperties": {
|
|
20
|
+
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"minItems": 1
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"required": ["techniques"],
|
|
27
|
+
"additionalProperties": false
|
|
28
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# techniques@1
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
The `techniques@1` stack enables recipes to declare cooking techniques used in the recipe, with optional descriptions.
|
|
5
|
+
|
|
6
|
+
## Adds
|
|
7
|
+
- Top-level `techniques` array with technique objects.
|
|
8
|
+
- Each technique includes `id` and `name` fields, with optional `description` and `metadata`.
|
|
9
|
+
|
|
10
|
+
## Requires
|
|
11
|
+
- None
|
|
12
|
+
|
|
13
|
+
## Semantics
|
|
14
|
+
- MUST: The `techniques` array must contain at least one technique.
|
|
15
|
+
- MUST: Each technique must include `id` and `name` fields.
|
|
16
|
+
- NOTE: Technique IDs may be referenced in step `techniqueIds` arrays when `structured@1` is present.
|
|
17
|
+
|
|
18
|
+
## Composition Notes
|
|
19
|
+
- This stack is monotonic: it adds requirements or fields without removing expressiveness.
|
|
20
|
+
- Interaction: Works with `structured@1` to enable step-level technique references. No hard dependencies on other stacks.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://soustack.spec/stacks/timed.schema.json",
|
|
4
|
+
"title": "Timed Stack",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"instructions": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"items": {
|
|
10
|
+
"anyOf": [
|
|
11
|
+
{ "$ref": "#/$defs/timedStep" },
|
|
12
|
+
{ "$ref": "#/$defs/timedStepSection" }
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"required": ["instructions"],
|
|
18
|
+
"allOf": [
|
|
19
|
+
{ "$ref": "./structured.schema.json" }
|
|
20
|
+
],
|
|
21
|
+
"$defs": {
|
|
22
|
+
"timedStep": {
|
|
23
|
+
"allOf": [
|
|
24
|
+
{ "$ref": "../defs/entities.schema.json#/$defs/StepBase" },
|
|
25
|
+
{
|
|
26
|
+
"properties": {
|
|
27
|
+
"timing": {
|
|
28
|
+
"allOf": [
|
|
29
|
+
{ "$ref": "../defs/entities.schema.json#/$defs/StepBase/properties/timing" },
|
|
30
|
+
{ "required": ["activity"], "anyOf": [ { "required": ["duration"] }, { "required": ["completionCue"] } ] }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"required": ["id", "timing"]
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"timedStepSection": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"properties": {
|
|
41
|
+
"section": { "type": "string" },
|
|
42
|
+
"steps": {
|
|
43
|
+
"type": "array",
|
|
44
|
+
"items": {
|
|
45
|
+
"anyOf": [
|
|
46
|
+
{ "$ref": "#/$defs/timedStep" },
|
|
47
|
+
{ "$ref": "#/$defs/timedStepSection" }
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"metadata": { "type": "object", "additionalProperties": true }
|
|
52
|
+
},
|
|
53
|
+
"required": ["section", "steps"],
|
|
54
|
+
"additionalProperties": false,
|
|
55
|
+
"patternProperties": {
|
|
56
|
+
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# timed@1
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
The `timed@1` stack enables recipes to include timing information for steps, supporting scheduling and time-based planning.
|
|
5
|
+
|
|
6
|
+
## Adds
|
|
7
|
+
- Step objects must include a `timing` field with `activity` (active/passive) and either `duration` or `completionCue`.
|
|
8
|
+
- Timing may include duration ranges (minMinutes/maxMinutes) or completion cues.
|
|
9
|
+
|
|
10
|
+
## Requires
|
|
11
|
+
- `structured@1`
|
|
12
|
+
|
|
13
|
+
## Semantics
|
|
14
|
+
- MUST: Each step must include a `timing` object with `activity` field.
|
|
15
|
+
- MUST: Each timing object must include either `duration` or `completionCue`.
|
|
16
|
+
- NOTE: This stack implies `structured@1` (steps are objects with IDs).
|
|
17
|
+
|
|
18
|
+
## Composition Notes
|
|
19
|
+
- This stack is monotonic: it adds requirements or fields without removing expressiveness.
|
|
20
|
+
- Interaction: Required by `compute@1` for deterministic scheduling. Works with `quantified@1` for computational recipe planning.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://soustack.spec/defs/common.schema.json",
|
|
4
|
+
"title": "Common definitions for Soustack",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"stackId": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"anyOf": [
|
|
10
|
+
{
|
|
11
|
+
"enum": [
|
|
12
|
+
"quantified@1",
|
|
13
|
+
"structured@1",
|
|
14
|
+
"timed@1",
|
|
15
|
+
"referenced@1",
|
|
16
|
+
"compute@1",
|
|
17
|
+
"storage@1",
|
|
18
|
+
"dietary@1",
|
|
19
|
+
"substitutions@1",
|
|
20
|
+
"techniques@1",
|
|
21
|
+
"illustrated@1",
|
|
22
|
+
"scaling@1",
|
|
23
|
+
"nutrition@1"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"pattern": "^x-[A-Za-z0-9_-]+@[0-9]+$",
|
|
28
|
+
"description": "Extension stacks must use x- prefix and include a major version"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"extensionLane": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"pattern": "^x-[A-Za-z0-9_-]+$"
|
|
35
|
+
},
|
|
36
|
+
"extensionLaneValue": {
|
|
37
|
+
"description": "Accept any JSON value for extension lanes while keeping the top-level property name constrained.",
|
|
38
|
+
"type": ["object", "array", "string", "number", "boolean", "null"]
|
|
39
|
+
},
|
|
40
|
+
"uri": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"format": "uri"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"additionalProperties": false
|
|
46
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://soustack.spec/defs/duration.schema.json",
|
|
4
|
+
"title": "Durations",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"DurationMinutes": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"minutes": { "type": "number", "exclusiveMinimum": 0 },
|
|
11
|
+
"metadata": { "type": "object", "additionalProperties": true }
|
|
12
|
+
},
|
|
13
|
+
"required": ["minutes"],
|
|
14
|
+
"additionalProperties": false,
|
|
15
|
+
"patternProperties": {
|
|
16
|
+
"^x-": { "$ref": "./common.schema.json#/properties/extensionLaneValue" }
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"StorageDuration": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"iso8601": { "type": "string", "pattern": "^P" },
|
|
23
|
+
"metadata": { "type": "object", "additionalProperties": true }
|
|
24
|
+
},
|
|
25
|
+
"required": ["iso8601"],
|
|
26
|
+
"additionalProperties": false,
|
|
27
|
+
"patternProperties": {
|
|
28
|
+
"^x-": { "$ref": "./common.schema.json#/properties/extensionLaneValue" }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"additionalProperties": false
|
|
33
|
+
}
|