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/dist/index.d.mts CHANGED
@@ -1,13 +1,19 @@
1
1
  /**
2
- * Soustack Recipe Schema v0.1
2
+ * Soustack Recipe Schema v0.2.1
3
3
  * A portable, scalable, interoperable recipe format.
4
4
  */
5
5
  interface SoustackRecipe {
6
+ /** Optional $schema pointer for profile-aware validation */
7
+ $schema?: string;
6
8
  /** Unique identifier (slug or UUID) */
7
9
  id?: string;
10
+ /** Optional display title */
11
+ title?: string;
8
12
  /** The title of the recipe */
9
13
  name: string;
10
14
  /** Semantic versioning (e.g., 1.0.0) */
15
+ recipeVersion?: string;
16
+ /** Deprecated alias for recipeVersion */
11
17
  version?: string;
12
18
  description?: string;
13
19
  /** Primary category (e.g., "Main Course") */
@@ -29,6 +35,8 @@ interface SoustackRecipe {
29
35
  storage?: Storage;
30
36
  substitutions?: Substitution[];
31
37
  nutrition?: NutritionFacts;
38
+ metadata?: Record<string, unknown>;
39
+ [k: `x-${string}`]: unknown;
32
40
  }
33
41
  type Recipe = SoustackRecipe;
34
42
  interface Source {
@@ -162,7 +170,7 @@ interface SoustackInstruction {
162
170
  }
163
171
  type Instruction = SoustackInstruction;
164
172
  interface StepTiming {
165
- duration: number;
173
+ duration: number | string;
166
174
  type: "active" | "passive";
167
175
  scaling?: "linear" | "fixed" | "sqrt";
168
176
  }
@@ -211,46 +219,43 @@ interface NutritionFacts {
211
219
  [key: string]: string | number | null | string[] | undefined;
212
220
  }
213
221
 
214
- /**
215
- * A "Computed Recipe" is the result of running the parser.
216
- * It is flat, strict, and ready for the UI to render.
217
- */
218
- interface ComputedRecipe {
219
- metadata: {
220
- targetYield: number;
221
- baseYield: number;
222
- multiplier: number;
223
- };
224
- ingredients: ComputedIngredient[];
225
- instructions: ComputedInstruction[];
226
- timing: {
227
- active: number;
228
- passive: number;
229
- total: number;
222
+ interface ScaleRecipeOptions {
223
+ multiplier?: number;
224
+ targetYield?: {
225
+ amount: number;
226
+ unit?: string;
230
227
  };
231
228
  }
232
- interface ComputedIngredient {
233
- id: string;
234
- name: string;
235
- amount: number;
236
- unit: string | null;
237
- text: string;
238
- notes?: string;
239
- }
240
- interface ComputedInstruction {
241
- id: string;
242
- text: string;
243
- durationMinutes: number;
244
- type: 'active' | 'passive';
245
- }
246
- declare function scaleRecipe(recipe: Recipe, targetYieldAmount: number): ComputedRecipe;
229
+ declare function scaleRecipe(recipe: Recipe, options?: ScaleRecipeOptions): Recipe;
247
230
 
248
- declare function validateRecipe(data: any): data is Recipe;
231
+ type ProfileName = "base" | "cookable" | "scalable" | "quantified" | "illustrated" | "schedulable";
232
+ interface NormalizedError {
233
+ path: string;
234
+ message: string;
235
+ keyword?: string;
236
+ }
237
+ interface NormalizedWarning {
238
+ path: string;
239
+ message: string;
240
+ }
241
+ interface ValidateOptions {
242
+ profile?: ProfileName;
243
+ schema?: string;
244
+ collectAllErrors?: boolean;
245
+ }
246
+ interface ValidationResult {
247
+ valid: boolean;
248
+ errors: NormalizedError[];
249
+ warnings: NormalizedWarning[];
250
+ normalized?: Recipe;
251
+ }
252
+ declare function validateRecipe(input: any, options?: ValidateOptions): ValidationResult;
253
+ declare function detectProfiles(recipe: any): ProfileName[];
249
254
 
250
255
  declare function fromSchemaOrg(input: unknown): Recipe | null;
251
256
 
252
257
  interface SchemaOrgRecipe$1 {
253
- '@context'?: string;
258
+ '@context'?: string | Array<string | Record<string, unknown>> | Record<string, unknown>;
254
259
  '@type'?: string | string[];
255
260
  name: string;
256
261
  description?: string;
@@ -295,6 +300,12 @@ interface HowToStep$1 {
295
300
  name?: string;
296
301
  url?: string;
297
302
  image?: SchemaOrgImage;
303
+ '@id'?: string;
304
+ id?: string;
305
+ totalTime?: string;
306
+ performTime?: string;
307
+ prepTime?: string;
308
+ duration?: string;
298
309
  }
299
310
  interface HowToSection {
300
311
  '@type': 'HowToSection';
@@ -334,110 +345,9 @@ interface SchemaOrgRecipe {
334
345
  aggregateRating?: unknown;
335
346
  [key: string]: unknown;
336
347
  }
337
- interface FetchRequestInit {
338
- headers?: Record<string, string>;
339
- signal?: AbortSignal;
340
- redirect?: 'follow' | 'error' | 'manual';
341
- }
342
- interface FetchResponse {
343
- ok: boolean;
344
- status: number;
345
- statusText: string;
346
- text(): Promise<string>;
347
- }
348
- type FetchImplementation = (url: string, init?: FetchRequestInit) => Promise<FetchResponse>;
349
- interface FetchOptions {
350
- timeout?: number;
351
- userAgent?: string;
352
- maxRetries?: number;
353
- fetchFn?: FetchImplementation;
354
- }
355
- interface ScrapeRecipeOptions extends FetchOptions {
356
- }
357
348
 
358
- /**
359
- * Scrapes a recipe from a URL (Node.js only).
360
- *
361
- * ⚠️ Not available in browser environments due to CORS restrictions.
362
- * For browser usage, fetch the HTML yourself and use extractRecipeFromHTML().
363
- *
364
- * @param url - The URL of the recipe page to scrape
365
- * @param options - Fetch options (timeout, userAgent, maxRetries)
366
- * @returns A Soustack recipe object
367
- * @throws Error if no recipe is found
368
- */
369
- declare function scrapeRecipe(url: string, options?: ScrapeRecipeOptions): Promise<Recipe>;
370
- /**
371
- * Extracts a recipe from HTML string (browser and Node.js compatible).
372
- *
373
- * This function works in both environments and doesn't require network access.
374
- * Perfect for browser usage where you fetch HTML yourself (with cookies/session).
375
- *
376
- * @example
377
- * ```ts
378
- * // In browser:
379
- * const response = await fetch('https://example.com/recipe');
380
- * const html = await response.text();
381
- * const recipe = extractRecipeFromHTML(html);
382
- * ```
383
- *
384
- * @param html - The HTML string containing Schema.org recipe data
385
- * @returns A Soustack recipe object
386
- * @throws Error if no recipe is found
387
- */
388
- declare function extractRecipeFromHTML(html: string): Recipe;
389
- /**
390
- * Extract Schema.org recipe data from HTML string (browser-compatible).
391
- *
392
- * Returns the raw Schema.org recipe object, which can then be converted
393
- * to Soustack format using fromSchemaOrg(). This gives you access to the
394
- * original Schema.org data for inspection, debugging, or custom transformations.
395
- *
396
- * @param html - HTML string containing Schema.org recipe data
397
- * @returns Schema.org recipe object, or null if not found
398
- *
399
- * @example
400
- * ```ts
401
- * // In browser:
402
- * const response = await fetch('https://example.com/recipe');
403
- * const html = await response.text();
404
- * const schemaOrgRecipe = extractSchemaOrgRecipeFromHTML(html);
405
- *
406
- * if (schemaOrgRecipe) {
407
- * // Inspect or modify Schema.org data before converting
408
- * console.log('Found recipe:', schemaOrgRecipe.name);
409
- *
410
- * // Convert to Soustack format
411
- * const soustackRecipe = fromSchemaOrg(schemaOrgRecipe);
412
- * }
413
- * ```
414
- */
415
349
  declare function extractSchemaOrgRecipeFromHTML(html: string): SchemaOrgRecipe | null;
416
350
 
417
- declare function normalizeIngredientInput(input: string): string;
418
- declare function parseIngredient(text: string): ParsedIngredient;
419
- declare function parseIngredientLine(text: string): ParsedIngredient;
420
- declare function parseIngredients(texts: string[]): ParsedIngredient[];
421
-
422
- declare function parseDuration(iso: string): number | null;
423
- declare function parseDuration(iso: string | null | undefined): number | null;
424
- declare function formatDuration(minutes: number): string;
425
- declare function formatDuration(minutes: number | null | undefined): string;
426
- declare function parseHumanDuration(text: string): number | null;
427
- declare function parseHumanDuration(text: string | null | undefined): number | null;
428
- declare function smartParseDuration(input: string): number | null;
429
- declare function smartParseDuration(input: string | null | undefined): number | null;
430
-
431
- declare function normalizeYield(text: string): string;
432
- declare function parseYield(text: string): ParsedYield | null;
433
- declare function formatYield(value: ParsedYield): string;
434
-
435
- /**
436
- * Normalize Schema.org image formats to Soustack format.
437
- * - String values pass through
438
- * - Arrays collapse to string or string[] after URL extraction
439
- * - ImageObjects extract their url/contentUrl
440
- */
441
- declare function normalizeImage(image: SchemaOrgImage | undefined | null): string | string[] | undefined;
351
+ declare const SOUSTACK_SPEC_VERSION = "0.2.1";
442
352
 
443
- export { type Alternative, type ComputedIngredient, type ComputedInstruction, type ComputedRecipe, type Equipment, type FrozenStorageMethod, type Ingredient, type IngredientItem, type IngredientSubsection, type Instruction, type InstructionItem, type InstructionSubsection, type MakeAheadComponent, type NutritionFacts, type ParsedIngredient, type ParsedYield, type Quantity, type Recipe, type Scaling, type ScalingBakersPercentage, type ScalingBase, type ScalingDiscrete, type ScalingFixed, type ScalingLinear, type ScalingProportional, type SchemaOrgRecipe, type SimpleTime, type Source, type SoustackInstruction, type SoustackRecipe, type StepTiming, type Storage, type StorageMethod, type StructuredTime, type Substitution, type Time, type Yield, extractRecipeFromHTML, extractSchemaOrgRecipeFromHTML, formatDuration, formatYield, fromSchemaOrg, normalizeImage, normalizeIngredientInput, normalizeYield, parseDuration, parseHumanDuration, parseIngredient, parseIngredientLine, parseIngredients, parseYield, scaleRecipe, scrapeRecipe, smartParseDuration, toSchemaOrg, validateRecipe };
353
+ export { type Alternative, type Equipment, type FrozenStorageMethod, type Ingredient, type IngredientItem, type IngredientSubsection, type Instruction, type InstructionItem, type InstructionSubsection, type MakeAheadComponent, type NutritionFacts, type ParsedIngredient, type ParsedYield, type Quantity, type Recipe, SOUSTACK_SPEC_VERSION, type Scaling, type ScalingBakersPercentage, type ScalingBase, type ScalingDiscrete, type ScalingFixed, type ScalingLinear, type ScalingProportional, type SimpleTime, type Source, type SoustackInstruction, type SoustackRecipe, type StepTiming, type Storage, type StorageMethod, type StructuredTime, type Substitution, type Time, type Yield, detectProfiles, extractSchemaOrgRecipeFromHTML, fromSchemaOrg, scaleRecipe, toSchemaOrg, validateRecipe };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,19 @@
1
1
  /**
2
- * Soustack Recipe Schema v0.1
2
+ * Soustack Recipe Schema v0.2.1
3
3
  * A portable, scalable, interoperable recipe format.
4
4
  */
5
5
  interface SoustackRecipe {
6
+ /** Optional $schema pointer for profile-aware validation */
7
+ $schema?: string;
6
8
  /** Unique identifier (slug or UUID) */
7
9
  id?: string;
10
+ /** Optional display title */
11
+ title?: string;
8
12
  /** The title of the recipe */
9
13
  name: string;
10
14
  /** Semantic versioning (e.g., 1.0.0) */
15
+ recipeVersion?: string;
16
+ /** Deprecated alias for recipeVersion */
11
17
  version?: string;
12
18
  description?: string;
13
19
  /** Primary category (e.g., "Main Course") */
@@ -29,6 +35,8 @@ interface SoustackRecipe {
29
35
  storage?: Storage;
30
36
  substitutions?: Substitution[];
31
37
  nutrition?: NutritionFacts;
38
+ metadata?: Record<string, unknown>;
39
+ [k: `x-${string}`]: unknown;
32
40
  }
33
41
  type Recipe = SoustackRecipe;
34
42
  interface Source {
@@ -162,7 +170,7 @@ interface SoustackInstruction {
162
170
  }
163
171
  type Instruction = SoustackInstruction;
164
172
  interface StepTiming {
165
- duration: number;
173
+ duration: number | string;
166
174
  type: "active" | "passive";
167
175
  scaling?: "linear" | "fixed" | "sqrt";
168
176
  }
@@ -211,46 +219,43 @@ interface NutritionFacts {
211
219
  [key: string]: string | number | null | string[] | undefined;
212
220
  }
213
221
 
214
- /**
215
- * A "Computed Recipe" is the result of running the parser.
216
- * It is flat, strict, and ready for the UI to render.
217
- */
218
- interface ComputedRecipe {
219
- metadata: {
220
- targetYield: number;
221
- baseYield: number;
222
- multiplier: number;
223
- };
224
- ingredients: ComputedIngredient[];
225
- instructions: ComputedInstruction[];
226
- timing: {
227
- active: number;
228
- passive: number;
229
- total: number;
222
+ interface ScaleRecipeOptions {
223
+ multiplier?: number;
224
+ targetYield?: {
225
+ amount: number;
226
+ unit?: string;
230
227
  };
231
228
  }
232
- interface ComputedIngredient {
233
- id: string;
234
- name: string;
235
- amount: number;
236
- unit: string | null;
237
- text: string;
238
- notes?: string;
239
- }
240
- interface ComputedInstruction {
241
- id: string;
242
- text: string;
243
- durationMinutes: number;
244
- type: 'active' | 'passive';
245
- }
246
- declare function scaleRecipe(recipe: Recipe, targetYieldAmount: number): ComputedRecipe;
229
+ declare function scaleRecipe(recipe: Recipe, options?: ScaleRecipeOptions): Recipe;
247
230
 
248
- declare function validateRecipe(data: any): data is Recipe;
231
+ type ProfileName = "base" | "cookable" | "scalable" | "quantified" | "illustrated" | "schedulable";
232
+ interface NormalizedError {
233
+ path: string;
234
+ message: string;
235
+ keyword?: string;
236
+ }
237
+ interface NormalizedWarning {
238
+ path: string;
239
+ message: string;
240
+ }
241
+ interface ValidateOptions {
242
+ profile?: ProfileName;
243
+ schema?: string;
244
+ collectAllErrors?: boolean;
245
+ }
246
+ interface ValidationResult {
247
+ valid: boolean;
248
+ errors: NormalizedError[];
249
+ warnings: NormalizedWarning[];
250
+ normalized?: Recipe;
251
+ }
252
+ declare function validateRecipe(input: any, options?: ValidateOptions): ValidationResult;
253
+ declare function detectProfiles(recipe: any): ProfileName[];
249
254
 
250
255
  declare function fromSchemaOrg(input: unknown): Recipe | null;
251
256
 
252
257
  interface SchemaOrgRecipe$1 {
253
- '@context'?: string;
258
+ '@context'?: string | Array<string | Record<string, unknown>> | Record<string, unknown>;
254
259
  '@type'?: string | string[];
255
260
  name: string;
256
261
  description?: string;
@@ -295,6 +300,12 @@ interface HowToStep$1 {
295
300
  name?: string;
296
301
  url?: string;
297
302
  image?: SchemaOrgImage;
303
+ '@id'?: string;
304
+ id?: string;
305
+ totalTime?: string;
306
+ performTime?: string;
307
+ prepTime?: string;
308
+ duration?: string;
298
309
  }
299
310
  interface HowToSection {
300
311
  '@type': 'HowToSection';
@@ -334,110 +345,9 @@ interface SchemaOrgRecipe {
334
345
  aggregateRating?: unknown;
335
346
  [key: string]: unknown;
336
347
  }
337
- interface FetchRequestInit {
338
- headers?: Record<string, string>;
339
- signal?: AbortSignal;
340
- redirect?: 'follow' | 'error' | 'manual';
341
- }
342
- interface FetchResponse {
343
- ok: boolean;
344
- status: number;
345
- statusText: string;
346
- text(): Promise<string>;
347
- }
348
- type FetchImplementation = (url: string, init?: FetchRequestInit) => Promise<FetchResponse>;
349
- interface FetchOptions {
350
- timeout?: number;
351
- userAgent?: string;
352
- maxRetries?: number;
353
- fetchFn?: FetchImplementation;
354
- }
355
- interface ScrapeRecipeOptions extends FetchOptions {
356
- }
357
348
 
358
- /**
359
- * Scrapes a recipe from a URL (Node.js only).
360
- *
361
- * ⚠️ Not available in browser environments due to CORS restrictions.
362
- * For browser usage, fetch the HTML yourself and use extractRecipeFromHTML().
363
- *
364
- * @param url - The URL of the recipe page to scrape
365
- * @param options - Fetch options (timeout, userAgent, maxRetries)
366
- * @returns A Soustack recipe object
367
- * @throws Error if no recipe is found
368
- */
369
- declare function scrapeRecipe(url: string, options?: ScrapeRecipeOptions): Promise<Recipe>;
370
- /**
371
- * Extracts a recipe from HTML string (browser and Node.js compatible).
372
- *
373
- * This function works in both environments and doesn't require network access.
374
- * Perfect for browser usage where you fetch HTML yourself (with cookies/session).
375
- *
376
- * @example
377
- * ```ts
378
- * // In browser:
379
- * const response = await fetch('https://example.com/recipe');
380
- * const html = await response.text();
381
- * const recipe = extractRecipeFromHTML(html);
382
- * ```
383
- *
384
- * @param html - The HTML string containing Schema.org recipe data
385
- * @returns A Soustack recipe object
386
- * @throws Error if no recipe is found
387
- */
388
- declare function extractRecipeFromHTML(html: string): Recipe;
389
- /**
390
- * Extract Schema.org recipe data from HTML string (browser-compatible).
391
- *
392
- * Returns the raw Schema.org recipe object, which can then be converted
393
- * to Soustack format using fromSchemaOrg(). This gives you access to the
394
- * original Schema.org data for inspection, debugging, or custom transformations.
395
- *
396
- * @param html - HTML string containing Schema.org recipe data
397
- * @returns Schema.org recipe object, or null if not found
398
- *
399
- * @example
400
- * ```ts
401
- * // In browser:
402
- * const response = await fetch('https://example.com/recipe');
403
- * const html = await response.text();
404
- * const schemaOrgRecipe = extractSchemaOrgRecipeFromHTML(html);
405
- *
406
- * if (schemaOrgRecipe) {
407
- * // Inspect or modify Schema.org data before converting
408
- * console.log('Found recipe:', schemaOrgRecipe.name);
409
- *
410
- * // Convert to Soustack format
411
- * const soustackRecipe = fromSchemaOrg(schemaOrgRecipe);
412
- * }
413
- * ```
414
- */
415
349
  declare function extractSchemaOrgRecipeFromHTML(html: string): SchemaOrgRecipe | null;
416
350
 
417
- declare function normalizeIngredientInput(input: string): string;
418
- declare function parseIngredient(text: string): ParsedIngredient;
419
- declare function parseIngredientLine(text: string): ParsedIngredient;
420
- declare function parseIngredients(texts: string[]): ParsedIngredient[];
421
-
422
- declare function parseDuration(iso: string): number | null;
423
- declare function parseDuration(iso: string | null | undefined): number | null;
424
- declare function formatDuration(minutes: number): string;
425
- declare function formatDuration(minutes: number | null | undefined): string;
426
- declare function parseHumanDuration(text: string): number | null;
427
- declare function parseHumanDuration(text: string | null | undefined): number | null;
428
- declare function smartParseDuration(input: string): number | null;
429
- declare function smartParseDuration(input: string | null | undefined): number | null;
430
-
431
- declare function normalizeYield(text: string): string;
432
- declare function parseYield(text: string): ParsedYield | null;
433
- declare function formatYield(value: ParsedYield): string;
434
-
435
- /**
436
- * Normalize Schema.org image formats to Soustack format.
437
- * - String values pass through
438
- * - Arrays collapse to string or string[] after URL extraction
439
- * - ImageObjects extract their url/contentUrl
440
- */
441
- declare function normalizeImage(image: SchemaOrgImage | undefined | null): string | string[] | undefined;
351
+ declare const SOUSTACK_SPEC_VERSION = "0.2.1";
442
352
 
443
- export { type Alternative, type ComputedIngredient, type ComputedInstruction, type ComputedRecipe, type Equipment, type FrozenStorageMethod, type Ingredient, type IngredientItem, type IngredientSubsection, type Instruction, type InstructionItem, type InstructionSubsection, type MakeAheadComponent, type NutritionFacts, type ParsedIngredient, type ParsedYield, type Quantity, type Recipe, type Scaling, type ScalingBakersPercentage, type ScalingBase, type ScalingDiscrete, type ScalingFixed, type ScalingLinear, type ScalingProportional, type SchemaOrgRecipe, type SimpleTime, type Source, type SoustackInstruction, type SoustackRecipe, type StepTiming, type Storage, type StorageMethod, type StructuredTime, type Substitution, type Time, type Yield, extractRecipeFromHTML, extractSchemaOrgRecipeFromHTML, formatDuration, formatYield, fromSchemaOrg, normalizeImage, normalizeIngredientInput, normalizeYield, parseDuration, parseHumanDuration, parseIngredient, parseIngredientLine, parseIngredients, parseYield, scaleRecipe, scrapeRecipe, smartParseDuration, toSchemaOrg, validateRecipe };
353
+ export { type Alternative, type Equipment, type FrozenStorageMethod, type Ingredient, type IngredientItem, type IngredientSubsection, type Instruction, type InstructionItem, type InstructionSubsection, type MakeAheadComponent, type NutritionFacts, type ParsedIngredient, type ParsedYield, type Quantity, type Recipe, SOUSTACK_SPEC_VERSION, type Scaling, type ScalingBakersPercentage, type ScalingBase, type ScalingDiscrete, type ScalingFixed, type ScalingLinear, type ScalingProportional, type SimpleTime, type Source, type SoustackInstruction, type SoustackRecipe, type StepTiming, type Storage, type StorageMethod, type StructuredTime, type Substitution, type Time, type Yield, detectProfiles, extractSchemaOrgRecipeFromHTML, fromSchemaOrg, scaleRecipe, toSchemaOrg, validateRecipe };