@recipe-book/core 0.1.0 → 0.1.2

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 (60) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/README.md +1 -1
  3. package/dist/{chunk-VWVIBCTG.cjs → chunk-ISRDJR2O.cjs} +227 -179
  4. package/dist/chunk-ISRDJR2O.cjs.map +1 -0
  5. package/dist/errors.js +29 -1
  6. package/dist/errors.js.map +1 -1
  7. package/dist/format/duration.js.map +1 -1
  8. package/dist/format/ingredient.js.map +1 -1
  9. package/dist/format/number.js +1 -1
  10. package/dist/format/number.js.map +1 -1
  11. package/dist/format/options.js.map +1 -1
  12. package/dist/format/quantity.js.map +1 -1
  13. package/dist/format/servings.js.map +1 -1
  14. package/dist/format/temperature.js.map +1 -1
  15. package/dist/index.cjs +87 -74
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +71 -28
  18. package/dist/index.d.ts +71 -28
  19. package/dist/index.js +6 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/ingredient/parse.js +27 -22
  22. package/dist/ingredient/parse.js.map +1 -1
  23. package/dist/issues.js.map +1 -1
  24. package/dist/quantity/amount.js +11 -0
  25. package/dist/quantity/amount.js.map +1 -1
  26. package/dist/quantity/parse.js +58 -81
  27. package/dist/quantity/parse.js.map +1 -1
  28. package/dist/quantity/scale.js +5 -2
  29. package/dist/quantity/scale.js.map +1 -1
  30. package/dist/recipe/ids.js.map +1 -1
  31. package/dist/recipe/parse.js +80 -60
  32. package/dist/recipe/parse.js.map +1 -1
  33. package/dist/recipe/schema.js +8 -6
  34. package/dist/recipe/schema.js.map +1 -1
  35. package/dist/resolve.js +6 -1
  36. package/dist/resolve.js.map +1 -1
  37. package/dist/schema-org/find.js +46 -13
  38. package/dist/schema-org/find.js.map +1 -1
  39. package/dist/schema-org/from-json-ld.js +134 -71
  40. package/dist/schema-org/from-json-ld.js.map +1 -1
  41. package/dist/schema-org/text.js.map +1 -1
  42. package/dist/schema-org/to-json-ld.js +12 -15
  43. package/dist/schema-org/to-json-ld.js.map +1 -1
  44. package/dist/schema-org.cjs +239 -147
  45. package/dist/schema-org.cjs.map +1 -1
  46. package/dist/schema-org.d.cts +35 -21
  47. package/dist/schema-org.d.ts +35 -21
  48. package/dist/steps/segment.js.map +1 -1
  49. package/dist/steps/temperature.js +13 -13
  50. package/dist/steps/temperature.js.map +1 -1
  51. package/dist/{types-DSMf45H5.d.cts → types-CKIoWsz8.d.cts} +25 -7
  52. package/dist/{types-DSMf45H5.d.ts → types-CKIoWsz8.d.ts} +25 -7
  53. package/dist/units/convert.js +16 -8
  54. package/dist/units/convert.js.map +1 -1
  55. package/dist/units/temperature.js +7 -1
  56. package/dist/units/temperature.js.map +1 -1
  57. package/dist/validation.js +1 -1
  58. package/dist/validation.js.map +1 -1
  59. package/package.json +1 -1
  60. package/dist/chunk-VWVIBCTG.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,21 +1,20 @@
1
1
  'use strict';
2
2
 
3
- var chunkVWVIBCTG_cjs = require('./chunk-VWVIBCTG.cjs');
3
+ var chunkISRDJR2O_cjs = require('./chunk-ISRDJR2O.cjs');
4
4
 
5
5
  // src/recipe/schema.ts
6
6
  var recipeSchema = /* @__PURE__ */ standardSchema();
7
7
  function createRecipeSchema(options) {
8
- return standardSchema({
9
- parseIngredientLine: chunkVWVIBCTG_cjs.readLineOption("createRecipeSchema", options)
10
- });
8
+ return standardSchema(chunkISRDJR2O_cjs.readLineOption("createRecipeSchema", options));
11
9
  }
12
- function standardSchema(options) {
10
+ function standardSchema(readLine) {
11
+ const lines = readLine ?? "use createRecipeSchema({ parseIngredientLine })";
13
12
  return {
14
13
  "~standard": {
15
14
  version: 1,
16
15
  vendor: "recipe-book",
17
16
  validate: (value) => {
18
- const result = chunkVWVIBCTG_cjs.parseRecipe(value, options);
17
+ const result = chunkISRDJR2O_cjs.parseRecipeWith(value, lines);
19
18
  if (result.success) {
20
19
  return { value: result.data };
21
20
  }
@@ -29,9 +28,9 @@ function standardSchema(options) {
29
28
 
30
29
  // src/units/convert.ts
31
30
  function convert(quantity, to) {
32
- const amount = chunkVWVIBCTG_cjs.checkQuantity("convert", quantity);
33
- const target = chunkVWVIBCTG_cjs.getUnit(to);
34
- const source = quantity.unit === void 0 ? void 0 : chunkVWVIBCTG_cjs.getUnit(quantity.unit);
31
+ const amount = chunkISRDJR2O_cjs.checkQuantity("convert", quantity);
32
+ const target = chunkISRDJR2O_cjs.getUnit(to);
33
+ const source = quantity.unit === void 0 ? void 0 : chunkISRDJR2O_cjs.getUnit(quantity.unit);
35
34
  if (source?.id === target.id) {
36
35
  return { amount, unit: to };
37
36
  }
@@ -41,46 +40,50 @@ function convert(quantity, to) {
41
40
  const fromBase = source.toBase;
42
41
  const toBase = target.toBase;
43
42
  return {
44
- amount: chunkVWVIBCTG_cjs.mapAmount(
45
- amount,
46
- (value) => value * fromBase / toBase
43
+ amount: chunkISRDJR2O_cjs.checkResult(
44
+ "convert",
45
+ chunkISRDJR2O_cjs.mapAmount(amount, (value) => value * fromBase / toBase)
47
46
  ),
48
47
  unit: to
49
48
  };
50
49
  }
51
50
  function canConvert(from, to) {
52
- const source = chunkVWVIBCTG_cjs.lookupUnit(from);
53
- const target = chunkVWVIBCTG_cjs.lookupUnit(to);
51
+ const source = chunkISRDJR2O_cjs.lookupUnit(from);
52
+ const target = chunkISRDJR2O_cjs.lookupUnit(to);
54
53
  if (!source || !target) {
55
54
  return false;
56
55
  }
57
56
  return source.id === target.id || source.toBase !== void 0 && source.dimension === target.dimension;
58
57
  }
59
58
  function toSystem(quantity, system) {
60
- chunkVWVIBCTG_cjs.checkQuantity("toSystem", quantity);
61
- chunkVWVIBCTG_cjs.checkSystem("toSystem", system);
62
- if (quantity.unit !== void 0 && !chunkVWVIBCTG_cjs.lookupUnit(quantity.unit)) {
63
- throw chunkVWVIBCTG_cjs.unknownUnitError(quantity.unit);
59
+ const amount = chunkISRDJR2O_cjs.checkQuantity("toSystem", quantity);
60
+ chunkISRDJR2O_cjs.checkSystem("toSystem", system);
61
+ const { unit } = quantity;
62
+ if (unit !== void 0 && !chunkISRDJR2O_cjs.lookupUnit(unit)) {
63
+ throw chunkISRDJR2O_cjs.unknownUnitError(unit);
64
64
  }
65
- return chunkVWVIBCTG_cjs.inSystem(quantity, system);
65
+ const checked = { amount, unit };
66
+ const result = chunkISRDJR2O_cjs.inSystem(checked, system);
67
+ chunkISRDJR2O_cjs.checkResult("toSystem", result.amount);
68
+ return result === checked ? quantity : result;
66
69
  }
67
70
  function incompatibleUnits(source, target) {
68
71
  const example = target.dimension === "mass" ? "120 g" : target.dimension === "volume" ? "250 ml" : `1 ${target.id}`;
69
72
  const fix = `Add an equivalent to the ingredient, e.g. equivalents: ["${example}"].`;
70
73
  if (!source) {
71
- return new chunkVWVIBCTG_cjs.RecipeBookError(
74
+ return new chunkISRDJR2O_cjs.RecipeBookError(
72
75
  "incompatible_units",
73
76
  `Cannot convert a quantity without a unit to ${target.id}; a bare count such as "2 eggs" has no fixed size. ${fix}`
74
77
  );
75
78
  }
76
79
  const countUnit = [source, target].find((unit) => unit.toBase === void 0);
77
80
  if (countUnit) {
78
- return new chunkVWVIBCTG_cjs.RecipeBookError(
81
+ return new chunkISRDJR2O_cjs.RecipeBookError(
79
82
  "incompatible_units",
80
83
  `Cannot convert ${source.id} to ${target.id}; ${countUnit.id} is a count unit with no fixed size. ${fix}`
81
84
  );
82
85
  }
83
- return new chunkVWVIBCTG_cjs.RecipeBookError(
86
+ return new chunkISRDJR2O_cjs.RecipeBookError(
84
87
  "incompatible_units",
85
88
  `Cannot convert ${source.id} (${source.dimension}) to ${target.id} (${target.dimension}). ${fix}`
86
89
  );
@@ -91,26 +94,32 @@ function convertTemperature(temperature, to) {
91
94
  const { value, unit } = temperature ?? {};
92
95
  if (typeof value !== "number" || !Number.isFinite(value)) {
93
96
  throw invalidArgument(
94
- `value must be a finite number, received ${chunkVWVIBCTG_cjs.describeValue(value)}`
97
+ `value must be a finite number, received ${chunkISRDJR2O_cjs.describeValue(value)}`
95
98
  );
96
99
  }
97
100
  if (unit !== "C" && unit !== "F") {
98
101
  throw invalidArgument(
99
- `temperature unit must be "C" or "F", received ${chunkVWVIBCTG_cjs.describeValue(unit)}`
102
+ `temperature unit must be "C" or "F", received ${chunkISRDJR2O_cjs.describeValue(unit)}`
100
103
  );
101
104
  }
102
105
  if (to !== "C" && to !== "F") {
103
106
  throw invalidArgument(
104
- `target unit must be "C" or "F", received ${chunkVWVIBCTG_cjs.describeValue(to)}`
107
+ `target unit must be "C" or "F", received ${chunkISRDJR2O_cjs.describeValue(to)}`
105
108
  );
106
109
  }
107
110
  if (unit === to) {
108
111
  return { value, unit };
109
112
  }
110
- return to === "C" ? { value: (value - 32) * 5 / 9, unit: "C" } : { value: value * 9 / 5 + 32, unit: "F" };
113
+ const result = to === "C" ? (value - 32) * 5 / 9 : value * 9 / 5 + 32;
114
+ if (!Number.isFinite(result)) {
115
+ throw invalidArgument(
116
+ `the result must be a finite number, but it is ${result}`
117
+ );
118
+ }
119
+ return { value: result, unit: to };
111
120
  }
112
121
  function invalidArgument(detail) {
113
- return new chunkVWVIBCTG_cjs.RecipeBookError(
122
+ return new chunkISRDJR2O_cjs.RecipeBookError(
114
123
  "invalid_argument",
115
124
  `convertTemperature: ${detail}.`
116
125
  );
@@ -122,27 +131,27 @@ function formatTemperature(temperature, options) {
122
131
  const fn = "formatTemperature";
123
132
  const { value, unit } = temperature ?? {};
124
133
  if (typeof value !== "number" || !Number.isFinite(value)) {
125
- throw new chunkVWVIBCTG_cjs.RecipeBookError(
134
+ throw new chunkISRDJR2O_cjs.RecipeBookError(
126
135
  "invalid_argument",
127
- `${fn}: value must be a finite number, received ${chunkVWVIBCTG_cjs.describeValue(value)}.`
136
+ `${fn}: value must be a finite number, received ${chunkISRDJR2O_cjs.describeValue(value)}.`
128
137
  );
129
138
  }
130
139
  if (unit !== "C" && unit !== "F") {
131
- throw new chunkVWVIBCTG_cjs.RecipeBookError(
140
+ throw new chunkISRDJR2O_cjs.RecipeBookError(
132
141
  "invalid_argument",
133
- `${fn}: temperature unit must be "C" or "F", received ${chunkVWVIBCTG_cjs.describeValue(unit)}.`
142
+ `${fn}: temperature unit must be "C" or "F", received ${chunkISRDJR2O_cjs.describeValue(unit)}.`
134
143
  );
135
144
  }
136
- const { number, unitDisplay } = chunkVWVIBCTG_cjs.readSettings(fn, options);
137
- const rounding = chunkVWVIBCTG_cjs.option(fn, "rounding", options?.rounding, [
145
+ const { number, unitDisplay } = chunkISRDJR2O_cjs.readSettings(fn, options);
146
+ const rounding = chunkISRDJR2O_cjs.option(fn, "rounding", options?.rounding, [
138
147
  "kitchen",
139
148
  "oven",
140
149
  "none"
141
150
  ]);
142
151
  const digits = rounding === "none" ? 3 : 0;
143
- const shown = rounding === "oven" ? chunkVWVIBCTG_cjs.round(value / OVEN_STEPS[unit], 0) * OVEN_STEPS[unit] : chunkVWVIBCTG_cjs.round(value, digits);
144
- const label = unitDisplay === "short" ? `\xB0${unit}` : `${chunkVWVIBCTG_cjs.pluralForm(shown) === "one" ? "degree" : "degrees"} ${unit === "C" ? "Celsius" : "Fahrenheit"}`;
145
- return `${number(shown, digits)}${chunkVWVIBCTG_cjs.NBSP}${label}`;
152
+ const shown = rounding === "oven" ? chunkISRDJR2O_cjs.round(value / OVEN_STEPS[unit], 0) * OVEN_STEPS[unit] : chunkISRDJR2O_cjs.round(value, digits);
153
+ const label = unitDisplay === "short" ? `\xB0${unit}` : `${chunkISRDJR2O_cjs.pluralForm(shown) === "one" ? "degree" : "degrees"} ${unit === "C" ? "Celsius" : "Fahrenheit"}`;
154
+ return `${number(shown, digits)}${chunkISRDJR2O_cjs.NBSP}${label}`;
146
155
  }
147
156
 
148
157
  // src/format/duration.ts
@@ -154,12 +163,12 @@ var SHORT_LABELS = {
154
163
  function formatDuration(minutes, options) {
155
164
  const fn = "formatDuration";
156
165
  if (!Number.isFinite(minutes) || minutes < 0) {
157
- throw new chunkVWVIBCTG_cjs.RecipeBookError(
166
+ throw new chunkISRDJR2O_cjs.RecipeBookError(
158
167
  "invalid_argument",
159
- `${fn}: minutes must be a finite number, 0 or more, received ${chunkVWVIBCTG_cjs.describeValue(minutes)}.`
168
+ `${fn}: minutes must be a finite number, 0 or more, received ${chunkISRDJR2O_cjs.describeValue(minutes)}.`
160
169
  );
161
170
  }
162
- const { number, unitDisplay } = chunkVWVIBCTG_cjs.readSettings(fn, options);
171
+ const { number, unitDisplay } = chunkISRDJR2O_cjs.readSettings(fn, options);
163
172
  const partDay = minutes % 1440;
164
173
  const seconds = Math.round(partDay * 60);
165
174
  const days = Math.round((minutes - partDay) / 1440) + (seconds === 86400 ? 1 : 0);
@@ -171,7 +180,7 @@ function formatDuration(minutes, options) {
171
180
  ];
172
181
  const parts = counts.filter(([, count]) => count > 0);
173
182
  return (parts.length > 0 ? parts : [["minute", 0]]).map(
174
- ([unit, count]) => `${number(count, 0)}${chunkVWVIBCTG_cjs.NBSP}${unitDisplay === "short" && SHORT_LABELS[unit] || (chunkVWVIBCTG_cjs.pluralForm(count) === "one" ? unit : `${unit}s`)}`
183
+ ([unit, count]) => `${number(count, 0)}${chunkISRDJR2O_cjs.NBSP}${unitDisplay === "short" && SHORT_LABELS[unit] || (chunkISRDJR2O_cjs.pluralForm(count) === "one" ? unit : `${unit}s`)}`
175
184
  ).join(" ");
176
185
  }
177
186
 
@@ -179,13 +188,13 @@ function formatDuration(minutes, options) {
179
188
  var LOOKBACK = 24;
180
189
  var MAX_ITEMS = 6;
181
190
  var NUMBER = /(\d{1,4})(\.\d{1,2})?(?![.,]?\d)/y;
182
- var DEGREES = /(?:-|\s{0,3})(?:([℃℉])|(?:[°º˚]|degrees?\b)(?:\s{0,3}(?:(cel[cs]ius|centigrade|fahrenheit|[cfс])(?!\w)|\(([cf])\)))?)/iy;
183
- var SCALE = /(\s{0,3})(cel[cs]ius|centigrade|fahrenheit|[cf])(?!\w)(?:°|\s{1,3}degrees?\b)?/iy;
184
- var FAN_AFTER = /(?:\s{1,3}|-)(?:(?:fan|convection)(?:[- ](?:forced|assisted|oven))?|umluft|hei(?:ß|ss)luft)\b/iy;
191
+ var DEGREES = /(?:-|\s{0,3})(?:([℃℉])|(?:[°º˚]|degrees?(?!(?!ι)[\p{sc=Latin}\p{sc=Cyrillic}\p{M}\d_]))(?:\s{0,3}(?:(cel[cs]ius|centigrade|fahrenheit|[cfс])(?!(?![ºªι])[\p{sc=Latin}\p{sc=Cyrillic}\p{M}\d_])|\(([cf])\)))?)/iuy;
192
+ var SCALE = /(\s{0,3})(cel[cs]ius|centigrade|fahrenheit|[cf])(?!(?![ºªι])[\p{sc=Latin}\p{sc=Cyrillic}\p{M}\d_])(?:°|\s{1,3}degrees?(?!(?!ι)[\p{sc=Latin}\p{sc=Cyrillic}\p{M}\d_]))?/iuy;
193
+ var FAN_AFTER = /(?:\s{1,3}|-)(?:(?:fan|convection)(?:[- ](?:forced|assisted|oven))?|umluft|hei(?:ß|ss)luft)(?!(?!ι)[\p{sc=Latin}\p{sc=Cyrillic}\p{M}\d_])/iuy;
185
194
  var FAN_GROUP = /\s{0,3}\(\s{0,2}(?:(?:fan|convection)(?:[- ](?:forced|assisted|oven))?|umluft|hei(?:ß|ss)luft)\s{0,2}\)/iy;
186
195
  var FAN_BEFORE = /(?:(?:fan|convection)(?:[- ](?:forced|assisted|oven))?|umluft|hei(?:ß|ss)luft)\s{1,3}/iy;
187
- var FAN_PREFIX = /\b(?:(?:fan|convection)(?:[- ](?:forced|assisted|oven))?|umluft|hei(?:ß|ss)luft)\s{1,3}$/i;
188
- var GAS = /(?:gas(?:\s{1,3}mark)?|mark)\s{0,3}(?:\d{1,2}(?:[-/\u2013]\d{1,2})?|[¼½¾])(?!\w)/iy;
196
+ var FAN_PREFIX = /(?:^|ι|[^\p{sc=Latin}\p{sc=Cyrillic}\p{M}\d_])((?:(?:fan|convection)(?:[- ](?:forced|assisted|oven))?|umluft|hei(?:ß|ss)luft)\s{1,3})$/iu;
197
+ var GAS = /(?:gas(?:\s{1,3}mark)?|mark)\s{0,3}(?:\d{1,2}(?:[-/\u2013]\d{1,2})?|[¼½¾])(?!(?!ι)[\p{sc=Latin}\p{sc=Cyrillic}\p{M}\d_])/iuy;
189
198
  var SEPARATOR = /\s{0,3}(?:(?:,?\s{0,3}or(?=\s)|[-/,\u2013\u2014])\s{0,3})?/iy;
190
199
  var OPEN = /\s{0,3}\(\s{0,2}(?:(?:about|around|roughly|approx(?:imately|\.)?|ca\.|i\.e\.,?|or)\s{1,3}|[~≈]\s{0,2})?/iy;
191
200
  var CLOSE = /\s{0,2}\)/y;
@@ -193,24 +202,24 @@ var OVEN_STEP = 10;
193
202
  var SHARED = /\d\s{0,3}(?:(?:[°º˚]|degrees?)\s{0,3}\(|(?:[°º˚]|degrees?)?\s{0,3}(?:[-\u2010-\u2015~/&]|to|and|or))\s{0,3}$/i;
194
203
  var GAS_ALTERNATIVE = /(?:gas|mark)\s{0,3}\d{1,2}\s{0,3}(?:\/|or)\s{0,3}$/i;
195
204
  var RANGE_AFTER = /\s{0,3}(?:[-\u2010-\u2015~/&]|to|and|or)\s{0,3}-?\d/iy;
196
- var NOT_A_READING = /\d[.,:]$|\bby\s{1,3}(?:(?:about|around|roughly|approximately|another|an?|further|additional|extra)\s{1,3}){0,3}$/i;
197
- var SETTING = /\b(?:at|to|of|be|is|until|oven|bake|roast|reads?|reach(?:es)?|registers?|hits?)\s{1,3}(?:(?:about|around|roughly|approximately)\s{1,3})?$/i;
198
- var OVEN_AFTER = /\s{1,3}oven\b/iy;
205
+ var NOT_A_READING = /\d[.,:]$|(?:^|ι|[^\p{sc=Latin}\p{sc=Cyrillic}\p{M}\d_])by\s{1,3}(?:(?:about|around|roughly|approximately|another|an?|further|additional|extra)\s{1,3}){0,3}$/iu;
206
+ var SETTING = /(?:^|ι|[^\p{sc=Latin}\p{sc=Cyrillic}\p{M}\d_])(?:at|to|of|be|is|until|oven|bake|roast|reads?|reach(?:es)?|registers?|hits?)\s{1,3}(?:(?:about|around|roughly|approximately)\s{1,3})?$/iu;
207
+ var OVEN_AFTER = /\s{1,3}oven(?!(?!ι)[\p{sc=Latin}\p{sc=Cyrillic}\p{M}\d_])/iuy;
199
208
  var SIGN = /[-\u2212]/;
200
209
  var SIGN_BEFORE = /[\s(]/;
201
210
  var LETTER = /[a-z]/i;
202
211
  function matchTemperature(text, digit, floor, assumed) {
203
- const negative = digit > floor && SIGN.test(text[digit - 1] ?? "") && SIGN_BEFORE.test(text[digit - 2] ?? " ");
212
+ const negative = digit > floor && SIGN.test(text[digit - 1] ?? "") && SIGN_BEFORE.test(text.charAt(digit - 2) || " ") && !SHARED.test(text.slice(Math.max(floor, digit - LOOKBACK), digit));
204
213
  const start = negative ? digit - 1 : digit;
205
214
  const before = text.slice(Math.max(floor, start - LOOKBACK), start);
206
- if (LETTER.test(text[start - 1] ?? "") || NOT_A_READING.test(before) || SHARED.test(before) && !GAS_ALTERNATIVE.test(before)) {
215
+ if (LETTER.test(text.charAt(start - 1)) || NOT_A_READING.test(before) || SHARED.test(before) && !GAS_ALTERNATIVE.test(before)) {
207
216
  return void 0;
208
217
  }
209
218
  const first = readingAt(text, digit);
210
219
  if (!first) {
211
220
  return void 0;
212
221
  }
213
- const prefix = FAN_PREFIX.exec(before)?.[0];
222
+ const prefix = FAN_PREFIX.exec(before)?.[1];
214
223
  const unit = first.unit ?? assumed;
215
224
  if (!unit || !first.unit && exec(RANGE_AFTER, text, first.end)) {
216
225
  return void 0;
@@ -224,7 +233,7 @@ function matchTemperature(text, digit, floor, assumed) {
224
233
  };
225
234
  const run = { readings: [reading], gas: false };
226
235
  let end = extend(text, first.end, run);
227
- if (first.cued && !run.readings[1] && !(prefix || SETTING.test(before) || exec(OVEN_AFTER, text, first.end))) {
236
+ if (first.cued && run.readings.length < 2 && !(prefix || SETTING.test(before) || exec(OVEN_AFTER, text, first.end))) {
228
237
  return void 0;
229
238
  }
230
239
  const fanEnd = text[end - 1] === ")" && !run.readings.some(({ fan }) => fan) ? at(FAN_AFTER, text, end) : void 0;
@@ -275,7 +284,7 @@ function extend(text, end, run, grouped) {
275
284
  if (next === void 0) {
276
285
  break;
277
286
  }
278
- owner = run.readings[size] ? [end, size] : void 0;
287
+ owner = run.readings.length > size ? [end, size] : void 0;
279
288
  end = next;
280
289
  }
281
290
  return end;
@@ -349,16 +358,16 @@ function at(pattern, text, index) {
349
358
  // src/steps/segment.ts
350
359
  function segmentStepText(text, options) {
351
360
  if (typeof text !== "string") {
352
- throw new chunkVWVIBCTG_cjs.RecipeBookError(
361
+ throw new chunkISRDJR2O_cjs.RecipeBookError(
353
362
  "invalid_argument",
354
- `segmentStepText: text must be a string, received ${chunkVWVIBCTG_cjs.describeValue(text)}.`
363
+ `segmentStepText: text must be a string, received ${chunkISRDJR2O_cjs.describeValue(text)}.`
355
364
  );
356
365
  }
357
366
  const assumed = options?.assumeTemperatureUnit;
358
367
  if (assumed !== void 0 && assumed !== "C" && assumed !== "F") {
359
- throw new chunkVWVIBCTG_cjs.RecipeBookError(
368
+ throw new chunkISRDJR2O_cjs.RecipeBookError(
360
369
  "invalid_argument",
361
- `segmentStepText: assumeTemperatureUnit must be "C" or "F", received ${chunkVWVIBCTG_cjs.describeValue(assumed)}.`
370
+ `segmentStepText: assumeTemperatureUnit must be "C" or "F", received ${chunkISRDJR2O_cjs.describeValue(assumed)}.`
362
371
  );
363
372
  }
364
373
  const segments = [];
@@ -388,71 +397,75 @@ function segmentStepText(text, options) {
388
397
 
389
398
  Object.defineProperty(exports, "RecipeBookError", {
390
399
  enumerable: true,
391
- get: function () { return chunkVWVIBCTG_cjs.RecipeBookError; }
400
+ get: function () { return chunkISRDJR2O_cjs.RecipeBookError; }
392
401
  });
393
402
  Object.defineProperty(exports, "RecipeValidationError", {
394
403
  enumerable: true,
395
- get: function () { return chunkVWVIBCTG_cjs.RecipeValidationError; }
404
+ get: function () { return chunkISRDJR2O_cjs.RecipeValidationError; }
396
405
  });
397
406
  Object.defineProperty(exports, "findUnit", {
398
407
  enumerable: true,
399
- get: function () { return chunkVWVIBCTG_cjs.findUnit; }
408
+ get: function () { return chunkISRDJR2O_cjs.findUnit; }
400
409
  });
401
410
  Object.defineProperty(exports, "formatIngredient", {
402
411
  enumerable: true,
403
- get: function () { return chunkVWVIBCTG_cjs.formatIngredient; }
412
+ get: function () { return chunkISRDJR2O_cjs.formatIngredient; }
404
413
  });
405
414
  Object.defineProperty(exports, "formatQuantity", {
406
415
  enumerable: true,
407
- get: function () { return chunkVWVIBCTG_cjs.formatQuantity; }
416
+ get: function () { return chunkISRDJR2O_cjs.formatQuantity; }
408
417
  });
409
418
  Object.defineProperty(exports, "formatQuantityToParts", {
410
419
  enumerable: true,
411
- get: function () { return chunkVWVIBCTG_cjs.formatQuantityToParts; }
420
+ get: function () { return chunkISRDJR2O_cjs.formatQuantityToParts; }
412
421
  });
413
422
  Object.defineProperty(exports, "formatServings", {
414
423
  enumerable: true,
415
- get: function () { return chunkVWVIBCTG_cjs.formatServings; }
424
+ get: function () { return chunkISRDJR2O_cjs.formatServings; }
416
425
  });
417
426
  Object.defineProperty(exports, "formatYield", {
418
427
  enumerable: true,
419
- get: function () { return chunkVWVIBCTG_cjs.formatYield; }
428
+ get: function () { return chunkISRDJR2O_cjs.formatYield; }
420
429
  });
421
430
  Object.defineProperty(exports, "getUnit", {
422
431
  enumerable: true,
423
- get: function () { return chunkVWVIBCTG_cjs.getUnit; }
432
+ get: function () { return chunkISRDJR2O_cjs.getUnit; }
424
433
  });
425
434
  Object.defineProperty(exports, "isRecipeBookError", {
426
435
  enumerable: true,
427
- get: function () { return chunkVWVIBCTG_cjs.isRecipeBookError; }
436
+ get: function () { return chunkISRDJR2O_cjs.isRecipeBookError; }
437
+ });
438
+ Object.defineProperty(exports, "isRecipeValidationError", {
439
+ enumerable: true,
440
+ get: function () { return chunkISRDJR2O_cjs.isRecipeValidationError; }
428
441
  });
429
442
  Object.defineProperty(exports, "parseIngredientLine", {
430
443
  enumerable: true,
431
- get: function () { return chunkVWVIBCTG_cjs.parseIngredientLine; }
444
+ get: function () { return chunkISRDJR2O_cjs.parseIngredientLine; }
432
445
  });
433
446
  Object.defineProperty(exports, "parseQuantity", {
434
447
  enumerable: true,
435
- get: function () { return chunkVWVIBCTG_cjs.parseQuantity; }
448
+ get: function () { return chunkISRDJR2O_cjs.parseQuantity; }
436
449
  });
437
450
  Object.defineProperty(exports, "parseRecipe", {
438
451
  enumerable: true,
439
- get: function () { return chunkVWVIBCTG_cjs.parseRecipe; }
452
+ get: function () { return chunkISRDJR2O_cjs.parseRecipe; }
440
453
  });
441
454
  Object.defineProperty(exports, "resolveIngredient", {
442
455
  enumerable: true,
443
- get: function () { return chunkVWVIBCTG_cjs.resolveIngredient; }
456
+ get: function () { return chunkISRDJR2O_cjs.resolveIngredient; }
444
457
  });
445
458
  Object.defineProperty(exports, "resolveRecipe", {
446
459
  enumerable: true,
447
- get: function () { return chunkVWVIBCTG_cjs.resolveRecipe; }
460
+ get: function () { return chunkISRDJR2O_cjs.resolveRecipe; }
448
461
  });
449
462
  Object.defineProperty(exports, "scaleQuantity", {
450
463
  enumerable: true,
451
- get: function () { return chunkVWVIBCTG_cjs.scaleQuantity; }
464
+ get: function () { return chunkISRDJR2O_cjs.scaleQuantity; }
452
465
  });
453
466
  Object.defineProperty(exports, "units", {
454
467
  enumerable: true,
455
- get: function () { return chunkVWVIBCTG_cjs.units; }
468
+ get: function () { return chunkISRDJR2O_cjs.units; }
456
469
  });
457
470
  exports.canConvert = canConvert;
458
471
  exports.convert = convert;