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.
- package/README.md +44 -27
- package/dist/cli/index.js +5225 -992
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +163 -91
- package/dist/index.d.ts +163 -91
- package/dist/index.js +5077 -1007
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5076 -1007
- package/dist/index.mjs.map +1 -1
- package/dist/{scrape.d.mts → scrape/index.d.mts} +88 -74
- package/dist/{scrape.d.ts → scrape/index.d.ts} +88 -74
- package/dist/{scrape.js → scrape/index.js} +255 -124
- package/dist/scrape/index.js.map +1 -0
- package/dist/{scrape.mjs → scrape/index.mjs} +255 -124
- package/dist/scrape/index.mjs.map +1 -0
- package/package.json +21 -9
- 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/dist/scrape.js.map +0 -1
- package/dist/scrape.mjs.map +0 -1
- 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,22 @@
|
|
|
1
|
+
# compute@1
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
The `compute@1` stack declares that a recipe meets prerequisites for computational operations such as scheduling, scaling, and deterministic planning.
|
|
5
|
+
|
|
6
|
+
## Adds
|
|
7
|
+
- No additional structural fields (claim stack).
|
|
8
|
+
|
|
9
|
+
## Requires
|
|
10
|
+
- `quantified@1`
|
|
11
|
+
- `timed@1`
|
|
12
|
+
|
|
13
|
+
## Semantics
|
|
14
|
+
- MUST: Recipe must satisfy all requirements of `quantified@1` and `timed@1`.
|
|
15
|
+
- NOTE: This is a claim stack indicating computational readiness. Tools SHOULD enforce that all prerequisites are met.
|
|
16
|
+
|
|
17
|
+
## Composition Notes
|
|
18
|
+
- This stack is monotonic: it adds requirements or fields without removing expressiveness.
|
|
19
|
+
- Interaction: Requires `quantified@1` for scaling inputs and `timed@1` for deterministic scheduling. Indicates recipe is ready for computational recipe planning tools.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://soustack.spec/stacks/dietary.schema.json",
|
|
4
|
+
"title": "Dietary Stack",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"dietary": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"basis": { "type": "string", "enum": ["perServing", "perRecipe"] },
|
|
11
|
+
"calories": { "type": "number", "minimum": 0 },
|
|
12
|
+
"macros": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"properties": {
|
|
15
|
+
"protein": { "type": "number", "minimum": 0 },
|
|
16
|
+
"fat": { "type": "number", "minimum": 0 },
|
|
17
|
+
"carbohydrates": { "type": "number", "minimum": 0 },
|
|
18
|
+
"metadata": { "type": "object", "additionalProperties": true }
|
|
19
|
+
},
|
|
20
|
+
"minProperties": 1,
|
|
21
|
+
"additionalProperties": false,
|
|
22
|
+
"patternProperties": {
|
|
23
|
+
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"diets": { "type": "array", "items": { "type": "string" } },
|
|
27
|
+
"allergens": { "type": "array", "items": { "type": "string" } },
|
|
28
|
+
"metadata": { "type": "object", "additionalProperties": true }
|
|
29
|
+
},
|
|
30
|
+
"required": ["basis"],
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"anyOf": [
|
|
33
|
+
{ "required": ["calories"] },
|
|
34
|
+
{ "required": ["macros"] },
|
|
35
|
+
{ "required": ["diets"] },
|
|
36
|
+
{ "required": ["allergens"] }
|
|
37
|
+
],
|
|
38
|
+
"patternProperties": {
|
|
39
|
+
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"required": ["dietary"],
|
|
44
|
+
"additionalProperties": false
|
|
45
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# dietary@1
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
The `dietary@1` stack enables recipes to declare nutritional information, dietary classifications, and allergen information.
|
|
5
|
+
|
|
6
|
+
## Adds
|
|
7
|
+
- Top-level `dietary` object with `basis` field (perServing or perRecipe).
|
|
8
|
+
- Optional `calories`, `macros` (protein, fat, carbohydrates), `diets`, and `allergens` fields.
|
|
9
|
+
- At least one of calories, macros, diets, or allergens must be present.
|
|
10
|
+
|
|
11
|
+
## Requires
|
|
12
|
+
- None
|
|
13
|
+
|
|
14
|
+
## Semantics
|
|
15
|
+
- MUST: The `dietary` object must include a `basis` field.
|
|
16
|
+
- MUST: At least one of `calories`, `macros`, `diets`, or `allergens` must be present.
|
|
17
|
+
- NOTE: Dietary tags and allergen lists are freeform strings in v1 (no controlled vocabulary).
|
|
18
|
+
|
|
19
|
+
## Composition Notes
|
|
20
|
+
- This stack is monotonic: it adds requirements or fields without removing expressiveness.
|
|
21
|
+
- Interaction: No dependencies on other stacks. Can be combined with any other stack.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://soustack.spec/stacks/equipment.schema.json",
|
|
4
|
+
"title": "Equipment Stack",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"equipment": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"minItems": 1,
|
|
10
|
+
"items": {
|
|
11
|
+
"anyOf": [
|
|
12
|
+
{ "type": "string", "minLength": 1 },
|
|
13
|
+
{ "$ref": "#/$defs/equipmentItem" }
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"required": ["equipment"],
|
|
19
|
+
"$defs": {
|
|
20
|
+
"equipmentItem": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"id": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"pattern": "^[A-Za-z0-9._-]+$",
|
|
26
|
+
"minLength": 1
|
|
27
|
+
},
|
|
28
|
+
"name": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"minLength": 1
|
|
31
|
+
},
|
|
32
|
+
"count": {
|
|
33
|
+
"type": "integer",
|
|
34
|
+
"minimum": 1
|
|
35
|
+
},
|
|
36
|
+
"countScaling": {
|
|
37
|
+
"oneOf": [
|
|
38
|
+
{ "type": "string", "enum": ["fixed", "linear"] },
|
|
39
|
+
{
|
|
40
|
+
"type": "object",
|
|
41
|
+
"properties": {
|
|
42
|
+
"mode": { "const": "threshold" },
|
|
43
|
+
"steps": {
|
|
44
|
+
"type": "array",
|
|
45
|
+
"minItems": 1,
|
|
46
|
+
"items": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"properties": {
|
|
49
|
+
"maxFactor": {
|
|
50
|
+
"type": "number",
|
|
51
|
+
"exclusiveMinimum": 0
|
|
52
|
+
},
|
|
53
|
+
"count": {
|
|
54
|
+
"type": "integer",
|
|
55
|
+
"minimum": 1
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"required": ["maxFactor", "count"],
|
|
59
|
+
"additionalProperties": false
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"required": ["mode", "steps"],
|
|
64
|
+
"additionalProperties": false
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"upgrades": {
|
|
69
|
+
"type": "array",
|
|
70
|
+
"minItems": 1,
|
|
71
|
+
"items": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"properties": {
|
|
74
|
+
"minFactor": {
|
|
75
|
+
"type": "number",
|
|
76
|
+
"exclusiveMinimum": 0
|
|
77
|
+
},
|
|
78
|
+
"use": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"pattern": "^[A-Za-z0-9._-]+$",
|
|
81
|
+
"minLength": 1
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"required": ["minFactor", "use"],
|
|
85
|
+
"additionalProperties": false
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"required": ["id", "name"],
|
|
90
|
+
"additionalProperties": false,
|
|
91
|
+
"patternProperties": {
|
|
92
|
+
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"additionalProperties": false
|
|
97
|
+
}
|
|
98
|
+
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#equipment@1
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
The `equipment@1` stack enables recipes to declare required equipment, with optional scaling-aware counts and upgrade paths for different scale factors.
|
|
8
|
+
|
|
9
|
+
This stack is adoption-first: equipment can be declared as simple strings for minimal friction, or as structured objects with IDs for cross-referencing from steps.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
A document that declares `equipment@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 `equipment@1` is declared, the document MUST include a top-level `equipment` array.
|
|
25
|
+
|
|
26
|
+
### Equipment Array
|
|
27
|
+
|
|
28
|
+
The `equipment` array contains one or more items. Each item MAY be:
|
|
29
|
+
|
|
30
|
+
• A string (simple tool name)
|
|
31
|
+
• A structured equipment object
|
|
32
|
+
|
|
33
|
+
### Structured Equipment Object
|
|
34
|
+
|
|
35
|
+
Structured equipment objects support:
|
|
36
|
+
|
|
37
|
+
• `id` (required): unique identifier following pattern `^[A-Za-z0-9._-]+$`
|
|
38
|
+
• `name` (required): human-readable name
|
|
39
|
+
• `count` (optional): integer >= 1, default 1
|
|
40
|
+
• `countScaling` (optional): scaling behavior for count
|
|
41
|
+
• `upgrades` (optional): array of upgrade rules
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Scaling Behavior
|
|
46
|
+
|
|
47
|
+
Equipment scaling is defined in terms of a scale factor F. If no scaling factor exists in the document, implementations MUST treat F = 1.
|
|
48
|
+
|
|
49
|
+
### countScaling
|
|
50
|
+
|
|
51
|
+
The `countScaling` field controls how equipment count changes with scale:
|
|
52
|
+
|
|
53
|
+
#### fixed
|
|
54
|
+
|
|
55
|
+
Count remains unchanged regardless of scale factor.
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"id": "pan",
|
|
60
|
+
"name": "8-inch skillet",
|
|
61
|
+
"count": 1,
|
|
62
|
+
"countScaling": "fixed"
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### linear
|
|
67
|
+
|
|
68
|
+
Effective count = ceil(count × F)
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"id": "mixing_bowl",
|
|
73
|
+
"name": "Mixing bowl",
|
|
74
|
+
"count": 2,
|
|
75
|
+
"countScaling": "linear"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### threshold
|
|
80
|
+
|
|
81
|
+
Pick the first step where F <= maxFactor, otherwise use the last step.
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"id": "sheet_pan",
|
|
86
|
+
"name": "Baking sheet",
|
|
87
|
+
"count": 1,
|
|
88
|
+
"countScaling": {
|
|
89
|
+
"mode": "threshold",
|
|
90
|
+
"steps": [
|
|
91
|
+
{ "maxFactor": 1.0, "count": 1 },
|
|
92
|
+
{ "maxFactor": 2.0, "count": 2 },
|
|
93
|
+
{ "maxFactor": 4.0, "count": 3 }
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The `steps` array MUST be non-empty. Steps SHOULD be in ascending order by `maxFactor` (semantic validation may enforce this in tooling).
|
|
100
|
+
|
|
101
|
+
### upgrades
|
|
102
|
+
|
|
103
|
+
The `upgrades` array allows swapping to a different equipment item at higher scale factors.
|
|
104
|
+
|
|
105
|
+
Choose the upgrade with the highest `minFactor` where `minFactor <= F`. If no upgrade matches, use the base equipment item.
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"id": "skillet_small",
|
|
110
|
+
"name": "8-inch skillet",
|
|
111
|
+
"upgrades": [
|
|
112
|
+
{ "minFactor": 2.0, "use": "skillet_large" }
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The `use` field MUST reference an equipment id that exists in the equipment array (semantic validation).
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Step Usage
|
|
122
|
+
|
|
123
|
+
When `structured@1` is present (steps are objects), steps MAY include an optional `usesEquipment` field.
|
|
124
|
+
|
|
125
|
+
Field
|
|
126
|
+
step.usesEquipment : array of equipment ids
|
|
127
|
+
|
|
128
|
+
Each id in `usesEquipment` MUST exist in the equipment array (semantic validation when both stacks are present).
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"equipment": [
|
|
135
|
+
{ "id": "skillet", "name": "8-inch skillet" }
|
|
136
|
+
],
|
|
137
|
+
"instructions": [
|
|
138
|
+
{
|
|
139
|
+
"id": "sear",
|
|
140
|
+
"text": "Sear the meat",
|
|
141
|
+
"usesEquipment": ["skillet"]
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Composition
|
|
150
|
+
|
|
151
|
+
The equipment stack is composable with other stacks:
|
|
152
|
+
|
|
153
|
+
• No hard dependency on `scaling@1` (scaling behavior uses factor if present, but does not require scaling stack)
|
|
154
|
+
• Works with `structured@1` to enable step-level equipment references
|
|
155
|
+
• Remains monotonic: does not close objects needed by other stacks
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Semantic Validation Rules (Normative)
|
|
160
|
+
|
|
161
|
+
Validators MUST enforce the following rules:
|
|
162
|
+
|
|
163
|
+
1. Equipment id uniqueness
|
|
164
|
+
All equipment object `id` values MUST be unique within the equipment array.
|
|
165
|
+
|
|
166
|
+
2. Step equipment references
|
|
167
|
+
If both `equipment@1` and `structured@1` are present, and a step includes `usesEquipment`, all referenced ids MUST exist in the equipment array (as object ids, not string items).
|
|
168
|
+
|
|
169
|
+
3. Upgrade references
|
|
170
|
+
If an equipment item includes `upgrades`, each `upgrades[].use` MUST reference an existing equipment object id.
|
|
171
|
+
|
|
172
|
+
4. Threshold steps ordering
|
|
173
|
+
For `countScaling.mode == "threshold"`, the `steps` array SHOULD be in ascending order by `maxFactor` (tooling may warn but not fail validation).
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Examples
|
|
178
|
+
|
|
179
|
+
Simple strings
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"equipment": ["mixing bowl", "whisk", "oven"]
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Structured with scaling
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"equipment": [
|
|
192
|
+
{
|
|
193
|
+
"id": "skillet",
|
|
194
|
+
"name": "8-inch skillet",
|
|
195
|
+
"count": 1,
|
|
196
|
+
"countScaling": "fixed"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"id": "bowl",
|
|
200
|
+
"name": "Mixing bowl",
|
|
201
|
+
"count": 2,
|
|
202
|
+
"countScaling": "linear"
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
With upgrades
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"equipment": [
|
|
213
|
+
{
|
|
214
|
+
"id": "skillet_small",
|
|
215
|
+
"name": "8-inch skillet",
|
|
216
|
+
"upgrades": [
|
|
217
|
+
{ "minFactor": 2.0, "use": "skillet_large" }
|
|
218
|
+
]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"id": "skillet_large",
|
|
222
|
+
"name": "12-inch skillet"
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Step usage
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"equipment": [
|
|
233
|
+
{ "id": "skillet", "name": "8-inch skillet" }
|
|
234
|
+
],
|
|
235
|
+
"instructions": [
|
|
236
|
+
{
|
|
237
|
+
"id": "sear",
|
|
238
|
+
"text": "Sear the meat in the skillet",
|
|
239
|
+
"usesEquipment": ["skillet"]
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://soustack.spec/stacks/illustrated.schema.json",
|
|
4
|
+
"title": "Illustrated Stack",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"images": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"items": { "$ref": "../defs/common.schema.json#/properties/uri" }
|
|
10
|
+
},
|
|
11
|
+
"videos": {
|
|
12
|
+
"type": "array",
|
|
13
|
+
"items": { "$ref": "../defs/common.schema.json#/properties/uri" }
|
|
14
|
+
},
|
|
15
|
+
"instructions": {
|
|
16
|
+
"type": "array",
|
|
17
|
+
"items": {
|
|
18
|
+
"anyOf": [
|
|
19
|
+
{ "$ref": "#/$defs/illustratedStep" },
|
|
20
|
+
{ "$ref": "#/$defs/illustratedSection" }
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"$defs": {
|
|
26
|
+
"illustratedStep": {
|
|
27
|
+
"allOf": [
|
|
28
|
+
{ "$ref": "../defs/entities.schema.json#/$defs/StepBase" },
|
|
29
|
+
{ "required": ["id"] }
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"illustratedSection": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"properties": {
|
|
35
|
+
"section": { "type": "string" },
|
|
36
|
+
"steps": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"items": {
|
|
39
|
+
"anyOf": [
|
|
40
|
+
{ "$ref": "#/$defs/illustratedStep" },
|
|
41
|
+
{ "$ref": "#/$defs/illustratedSection" }
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"metadata": { "type": "object", "additionalProperties": true }
|
|
46
|
+
},
|
|
47
|
+
"required": ["section", "steps"],
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"patternProperties": {
|
|
50
|
+
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# illustrated@1
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
The `illustrated@1` stack enables recipes to include media (images and videos) at the recipe and step levels for visual guidance.
|
|
5
|
+
|
|
6
|
+
## Adds
|
|
7
|
+
- Top-level `images` and `videos` arrays (optional).
|
|
8
|
+
- Step objects may include `images` and `videos` arrays.
|
|
9
|
+
- Steps are structured objects with `id` fields.
|
|
10
|
+
|
|
11
|
+
## Requires
|
|
12
|
+
- None
|
|
13
|
+
|
|
14
|
+
## Semantics
|
|
15
|
+
- MUST: At least one media item (image or video) must be present at the recipe level or in at least one step.
|
|
16
|
+
- MUST: Step objects must include `id` fields (steps are structured).
|
|
17
|
+
- NOTE: Media URIs must be valid according to the URI schema definition.
|
|
18
|
+
|
|
19
|
+
## Composition Notes
|
|
20
|
+
- This stack is monotonic: it adds requirements or fields without removing expressiveness.
|
|
21
|
+
- Interaction: Works with `structured@1` for step-level media. No dependencies on other stacks.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://soustack.spec/stacks/prep.schema.json",
|
|
4
|
+
"title": "Prep Stack",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"miseEnPlace": {
|
|
8
|
+
"type": "array",
|
|
9
|
+
"minItems": 1,
|
|
10
|
+
"items": {
|
|
11
|
+
"$ref": "#/$defs/miseEnPlaceTask"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"required": ["miseEnPlace"],
|
|
16
|
+
"$defs": {
|
|
17
|
+
"miseEnPlaceTask": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"id": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"pattern": "^[A-Za-z0-9._-]+$"
|
|
23
|
+
},
|
|
24
|
+
"text": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"minLength": 1
|
|
27
|
+
},
|
|
28
|
+
"inputs": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": {
|
|
31
|
+
"type": "string"
|
|
32
|
+
},
|
|
33
|
+
"uniqueItems": true,
|
|
34
|
+
"minItems": 1
|
|
35
|
+
},
|
|
36
|
+
"usesEquipment": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"items": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"pattern": "^[A-Za-z0-9._-]+$"
|
|
41
|
+
},
|
|
42
|
+
"uniqueItems": true,
|
|
43
|
+
"minItems": 1
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"required": ["text"],
|
|
47
|
+
"additionalProperties": false,
|
|
48
|
+
"patternProperties": {
|
|
49
|
+
"^x-": {
|
|
50
|
+
"$ref": "../defs/common.schema.json#/properties/extensionLaneValue"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"prepItem": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"properties": {
|
|
57
|
+
"verb": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"minLength": 1
|
|
60
|
+
},
|
|
61
|
+
"detail": {
|
|
62
|
+
"type": "string"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"required": ["verb"],
|
|
66
|
+
"additionalProperties": false,
|
|
67
|
+
"patternProperties": {
|
|
68
|
+
"^x-": {
|
|
69
|
+
"$ref": "../defs/common.schema.json#/properties/extensionLaneValue"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"additionalProperties": false
|
|
75
|
+
}
|
|
76
|
+
|