Skip to content

Commit 9c60ca2

Browse files
committed
Merge scrape-alias into main: Update README modules→stacks and CLI guidance
2 parents e6a5214 + e89a9cb commit 9c60ca2

2 files changed

Lines changed: 1044 additions & 1031 deletions

File tree

README.md

Lines changed: 49 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -98,19 +98,18 @@ if (!result.ok) {
9898
console.error('Profile validation failed', result.schemaErrors);
9999
}
100100

101-
// Validate with modules
102-
const recipeWithModules = {
101+
// Validate with stacks
102+
const recipeWithStacks = {
103103
profile: 'base',
104-
modules: ['nutrition@1', 'times@1'],
104+
stacks: { scaling: 1, storage: 1 },
105105
name: 'Test Recipe',
106-
ingredients: ['1 cup flour'],
106+
ingredients: [{ ingredient: 'flour', quantity: 1, unit: 'cup' }],
107107
instructions: ['Mix'],
108-
nutrition: { calories: 100, protein_g: 5 }, // Module payload required if declared
109-
times: { prepMinutes: 10, cookMinutes: 20, totalMinutes: 30 }, // v0.3: uses *Minutes fields
108+
// Stack payloads are validated when stacks are declared
110109
};
111-
const result2 = validateRecipe(recipeWithModules);
112-
// Validates using: base + profile + nutrition@1 module + times@1 module
113-
// Module contract: if module is declared, payload must exist (and vice versa)
110+
const result2 = validateRecipe(recipeWithStacks);
111+
// Validates using: base profile + scaling@1 stack + storage@1 stack
112+
// Stack contract: if stack is declared, corresponding payload must exist (and vice versa)
114113
```
115114

116115
### Imperial → metric ingredient conversion
@@ -147,7 +146,7 @@ The converter rounds using “sane” defaults (1 g/ml under 1 kg/1 L, the
147146
## Spec compatibility & bundled schemas
148147

149148
- Targets Soustack spec **v0.0.2** (`spec/SOUSTACK_SPEC_VERSION`, exported as `SOUSTACK_SPEC_VERSION`).
150-
- Ships the base schema, profile schemas, and module schemas in `spec/schemas/recipe/` and mirrors them into `src/schemas/recipe/` for consumers.
149+
- Ships the base schema, profile schemas, and stack schemas in `spec/stacks/` and mirrors them into `src/stacks/` for consumers.
151150
- Vendored fixtures live in `spec/fixtures` so tests can run offline, and version drift can be checked via `npm run validate:version`.
152151

153152
### Composed Validation Model
@@ -159,46 +158,51 @@ Soustack v0.0.2 uses a **composed validation model** where recipes are validated
159158
"allOf": [
160159
{ "$ref": "base.schema.json" },
161160
{ "$ref": "profiles/{profile}.schema.json" },
162-
{ "$ref": "modules/{module1}/{version}.schema.json" },
163-
{ "$ref": "modules/{module2}/{version}.schema.json" }
161+
{ "$ref": "stacks/{stack1}.schema.json" },
162+
{ "$ref": "stacks/{stack2}.schema.json" }
164163
]
165164
}
166165
```
167166

168167
The validator:
169-
- **Base schema**: Defines the core recipe structure (`@type`, `name`, `ingredients`, `instructions`, `profile`, `modules`)
168+
- **Base schema**: Defines the core recipe structure (`@type`, `name`, `ingredients`, `instructions`, `profile`, `stacks`)
170169
- **Profile overlay**: Adds profile-specific requirements (e.g., `base` or `lite`)
171-
- **Module overlays**: Each declared module adds its own validation rules
170+
- **Stack overlays**: Each declared stack adds its own validation rules
172171

173172
**Defaults:**
174173
- If `profile` is missing, it defaults to the schema bundle's configured default
175-
- If `modules` is missing, it defaults to `[]`
176-
177-
**Module Contract:** Modules enforce a symmetric contract:
178-
- If a module is declared in `modules`, the corresponding payload must exist
179-
- If a payload exists (e.g., `nutrition`, `times`), the module must be declared
180-
- The validator automatically infers modules from payloads and enforces this contract
181-
182-
**Caching:** Validators are cached by `${profile}::${sortedModules.join(",")}` for performance.
183-
184-
### Module Resolution
185-
186-
Modules are resolved to schema references using the pattern:
187-
- Module identifier format: `<name>@<version>` (e.g., `nutrition@1`, `schedule@1`)
188-
- Schema reference: `https://soustack.org/schemas/recipe/modules/<name>/<version>.schema.json`
189-
190-
The module registry (`schemas/registry/modules.json`) defines which modules are available and their properties, including:
191-
- `schemaOrgMappable`: Whether the module can be converted to Schema.org format
192-
- `minProfile`: Minimum profile required to use the module
193-
- `allowedOnLite`: Whether the module can be used with the lite profile
194-
195-
**Available Modules (v0.0.2):**
196-
- `attribution@1`: Source attribution (url, author, datePublished)
197-
- `taxonomy@1`: Classification (keywords, category, cuisine)
198-
- `media@1`: Images and videos (images, videos arrays)
199-
- `times@1`: Timing information (prepMinutes, cookMinutes, totalMinutes)
200-
- `nutrition@1`: Nutritional data (calories, protein_g as numbers)
201-
- `schedule@1`: Task scheduling (requires timed profile, includes instruction dependencies)
174+
- If `stacks` is missing, it defaults to `{}`
175+
176+
**Stack Contract:** Stacks enforce a symmetric contract:
177+
- If a stack is declared in `stacks`, the corresponding payload must exist (for non-structural stacks)
178+
- If a payload exists (e.g., `storage`, `equipment`), the stack must be declared
179+
- The validator automatically infers stacks from payloads and enforces this contract
180+
181+
**Caching:** Validators are cached by `${profile}::${sortedStackNames.join(",")}` for performance.
182+
183+
### Stack Resolution
184+
185+
Stacks are resolved to schema references using the pattern:
186+
- Stack declaration format: `{ "stackName": versionNumber }` (e.g., `{ "scaling": 1, "storage": 1 }`)
187+
- Schema reference: `https://spec.soustack.org/stacks/{stackName}.schema.json`
188+
189+
The stack registry (`stacks/registry.json`) defines which stacks are available and their properties, including:
190+
- Stack dependencies (e.g., `scaling` requires `quantified`)
191+
- Profile requirements (some stacks require specific profiles)
192+
193+
**Available Stacks (v0.0.2):**
194+
- `quantified`: Quantified ingredients with units
195+
- `scaling`: Scaling rules and modes
196+
- `structured`: Structured instructions with steps
197+
- `timed`: Timing information for instructions
198+
- `illustrated`: Images and videos
199+
- `equipment`: Required tools and equipment
200+
- `storage`: Storage instructions
201+
- `prep`: Prep guidance and mise en place
202+
- `dietary`: Dietary information
203+
- `substitutions`: Ingredient substitutions
204+
- `techniques`: Cooking techniques
205+
- `compute`: Computational recipe features
202206

203207
## Programmatic Usage
204208

@@ -271,7 +275,7 @@ async function convert(url: string) {
271275

272276
Use the helpers to move between Schema.org JSON-LD and Soustack's structured recipe format. The conversion automatically handles image normalization, supporting multiple image formats from Schema.org.
273277

274-
**BREAKING CHANGE in v0.0.2:** `toSchemaOrg()` now targets the **lite profile** and only includes modules that are marked as `schemaOrgMappable` in the modules registry. Non-mappable modules (e.g., `nutrition@1`, `schedule@1`) are excluded from the conversion.
278+
**BREAKING CHANGE in v0.0.2:** `toSchemaOrg()` now targets the **lite profile** and only includes stacks that can be mapped to Schema.org format. Non-mappable stacks are excluded from the conversion.
275279

276280
```ts
277281
import { fromSchemaOrg, toSchemaOrg, normalizeImage } from 'soustack';
@@ -392,11 +396,11 @@ npx soustack convert --from soustack --to schemaorg recipe.soustack.json -o reci
392396
# Scrape URLs (canonical workflow)
393397
npx soustack scrape <url> -o recipe.soustack.json
394398

395-
# Import from URL (alias for scrape, for compatibility)
399+
# Import from URL (optional alias for scrape, for compatibility)
396400
npx soustack import --url "https://example.com/recipe" -o recipe.soustack.json
397401

398-
# Bulk pipeline (delegates to @soustack/ingest)
399-
npx soustack ingest <source> [--out <path>] # requires @soustack/ingest
402+
# Bulk ingest pipeline (delegates to @soustack/ingest)
403+
npx soustack ingest <input> --out <dir> # requires @soustack/ingest
400404

401405
# Scale recipes
402406
npx soustack scale recipe.soustack.json 2

0 commit comments

Comments
 (0)