healthy-meals-core 0.0.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 (83) hide show
  1. package/dist/bmr.d.ts +3 -0
  2. package/dist/bmr.js +14 -0
  3. package/dist/dailyMealPlanGenerator.d.ts +3 -0
  4. package/dist/dailyMealPlanGenerator.js +211 -0
  5. package/dist/data/detailed-recipes-bs.json +417 -0
  6. package/dist/data/detailed-recipes.json +744 -0
  7. package/dist/foodConversion.d.ts +55 -0
  8. package/dist/foodConversion.js +200 -0
  9. package/dist/index.d.ts +12 -0
  10. package/dist/index.js +27 -0
  11. package/dist/planner.d.ts +2 -0
  12. package/dist/planner.js +24 -0
  13. package/dist/recipeBasedMealPlanGenerator.d.ts +17 -0
  14. package/dist/recipeBasedMealPlanGenerator.js +255 -0
  15. package/dist/recipeService.d.ts +28 -0
  16. package/dist/recipeService.js +136 -0
  17. package/dist/rules/cholesterol.d.ts +2 -0
  18. package/dist/rules/cholesterol.js +7 -0
  19. package/dist/rules/diabetes.d.ts +2 -0
  20. package/dist/rules/diabetes.js +7 -0
  21. package/dist/rules/fattyLiver.d.ts +2 -0
  22. package/dist/rules/fattyLiver.js +7 -0
  23. package/dist/rules/index.d.ts +7 -0
  24. package/dist/rules/index.js +47 -0
  25. package/dist/rules/lowCarb.d.ts +2 -0
  26. package/dist/rules/lowCarb.js +7 -0
  27. package/dist/rules/triglycerides.d.ts +2 -0
  28. package/dist/rules/triglycerides.js +7 -0
  29. package/dist/src/bmr.d.ts +3 -0
  30. package/dist/src/bmr.js +13 -0
  31. package/dist/src/dailyMealPlanGenerator.d.ts +3 -0
  32. package/dist/src/dailyMealPlanGenerator.js +210 -0
  33. package/dist/src/foodConversion.d.ts +55 -0
  34. package/dist/src/foodConversion.js +199 -0
  35. package/dist/src/index.d.ts +12 -0
  36. package/dist/src/index.js +27 -0
  37. package/dist/src/planner.d.ts +2 -0
  38. package/dist/src/planner.js +23 -0
  39. package/dist/src/recipeBasedMealPlanGenerator.d.ts +17 -0
  40. package/dist/src/recipeBasedMealPlanGenerator.js +254 -0
  41. package/dist/src/recipeService.d.ts +28 -0
  42. package/dist/src/recipeService.js +136 -0
  43. package/dist/src/rules/cholesterol.d.ts +2 -0
  44. package/dist/src/rules/cholesterol.js +6 -0
  45. package/dist/src/rules/diabetes.d.ts +2 -0
  46. package/dist/src/rules/diabetes.js +6 -0
  47. package/dist/src/rules/fattyLiver.d.ts +2 -0
  48. package/dist/src/rules/fattyLiver.js +6 -0
  49. package/dist/src/rules/index.d.ts +7 -0
  50. package/dist/src/rules/index.js +46 -0
  51. package/dist/src/rules/lowCarb.d.ts +2 -0
  52. package/dist/src/rules/lowCarb.js +6 -0
  53. package/dist/src/rules/triglycerides.d.ts +2 -0
  54. package/dist/src/rules/triglycerides.js +6 -0
  55. package/dist/src/types/firestore.d.ts +100 -0
  56. package/dist/src/types/firestore.js +2 -0
  57. package/dist/src/types/openfoodfacts.d.ts +113 -0
  58. package/dist/src/types/openfoodfacts.js +3 -0
  59. package/dist/src/types/recipe.d.ts +36 -0
  60. package/dist/src/types/recipe.js +2 -0
  61. package/dist/src/types.d.ts +24 -0
  62. package/dist/src/types.js +2 -0
  63. package/dist/src/variety.d.ts +17 -0
  64. package/dist/src/variety.js +129 -0
  65. package/dist/src/weeklyMealPlanGenerator.d.ts +3 -0
  66. package/dist/src/weeklyMealPlanGenerator.js +468 -0
  67. package/dist/src/weeklyPlanner.d.ts +12 -0
  68. package/dist/src/weeklyPlanner.js +31 -0
  69. package/dist/types/firestore.d.ts +100 -0
  70. package/dist/types/firestore.js +2 -0
  71. package/dist/types/openfoodfacts.d.ts +113 -0
  72. package/dist/types/openfoodfacts.js +3 -0
  73. package/dist/types/recipe.d.ts +36 -0
  74. package/dist/types/recipe.js +2 -0
  75. package/dist/types.d.ts +24 -0
  76. package/dist/types.js +2 -0
  77. package/dist/variety.d.ts +17 -0
  78. package/dist/variety.js +130 -0
  79. package/dist/weeklyMealPlanGenerator.d.ts +3 -0
  80. package/dist/weeklyMealPlanGenerator.js +469 -0
  81. package/dist/weeklyPlanner.d.ts +12 -0
  82. package/dist/weeklyPlanner.js +32 -0
  83. package/package.json +66 -0
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shuffleArray = shuffleArray;
4
+ exports.preventConsecutiveRepeats = preventConsecutiveRepeats;
5
+ exports.ensureMealVariety = ensureMealVariety;
6
+ exports.calculateMealDiversity = calculateMealDiversity;
7
+ exports.getMealCategory = getMealCategory;
8
+ exports.calculateCategoryDiversity = calculateCategoryDiversity;
9
+ exports.hasConsecutiveRepeats = hasConsecutiveRepeats;
10
+ exports.scoreMealPlan = scoreMealPlan;
11
+ exports.selectDiverseMeals = selectDiverseMeals;
12
+ function shuffleArray(array) {
13
+ const shuffled = [...array];
14
+ for (let i = shuffled.length - 1; i > 0; i--) {
15
+ const j = Math.floor(Math.random() * (i + 1));
16
+ [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
17
+ }
18
+ return shuffled;
19
+ }
20
+ function preventConsecutiveRepeats(meals) {
21
+ const result = [];
22
+ const used = new Set();
23
+ for (const meal of meals) {
24
+ if (result.length > 0 && result[result.length - 1].id === meal.id) {
25
+ continue;
26
+ }
27
+ result.push(meal);
28
+ used.add(meal.id);
29
+ }
30
+ return result;
31
+ }
32
+ function ensureMealVariety(meals, minVariety = 3) {
33
+ const mealCounts = new Map();
34
+ meals.forEach(meal => {
35
+ mealCounts.set(meal.id, (mealCounts.get(meal.id) || 0) + 1);
36
+ });
37
+ const overusedMeals = Array.from(mealCounts.entries())
38
+ .filter(([_, count]) => count > minVariety)
39
+ .map(([id]) => id);
40
+ if (overusedMeals.length === 0) {
41
+ return meals;
42
+ }
43
+ return meals.filter((meal, index) => {
44
+ if (!overusedMeals.includes(meal.id)) {
45
+ return true;
46
+ }
47
+ const previousOccurrences = meals
48
+ .slice(0, index)
49
+ .filter(m => m.id === meal.id).length;
50
+ return previousOccurrences < minVariety;
51
+ });
52
+ }
53
+ function calculateMealDiversity(meals) {
54
+ const uniqueMeals = new Set(meals.map(m => m.id));
55
+ return uniqueMeals.size / meals.length;
56
+ }
57
+ function getMealCategory(recipe) {
58
+ if (recipe.protein > 30)
59
+ return 'high-protein';
60
+ if (recipe.carbs > 50)
61
+ return 'high-carb';
62
+ if (recipe.fat > 20)
63
+ return 'high-fat';
64
+ return 'balanced';
65
+ }
66
+ function calculateCategoryDiversity(meals) {
67
+ const categories = meals.map(m => getMealCategory(m));
68
+ const uniqueCategories = new Set(categories);
69
+ return uniqueCategories.size / 4;
70
+ }
71
+ function hasConsecutiveRepeats(meals) {
72
+ for (let i = 1; i < meals.length; i++) {
73
+ if (meals[i].id === meals[i - 1].id) {
74
+ return true;
75
+ }
76
+ }
77
+ return false;
78
+ }
79
+ function scoreMealPlan(meals) {
80
+ const diversity = calculateMealDiversity(meals);
81
+ const categoryDiversity = calculateCategoryDiversity(meals);
82
+ const avgProtein = meals.reduce((sum, m) => sum + m.protein, 0) / meals.length;
83
+ const proteinVariance = meals.reduce((sum, m) => sum + Math.pow(m.protein - avgProtein, 2), 0) / meals.length;
84
+ const proteinBalance = 1 / (1 + proteinVariance / 100);
85
+ const avgCalories = meals.reduce((sum, m) => sum + m.calories, 0) / meals.length;
86
+ const calorieVariance = meals.reduce((sum, m) => sum + Math.pow(m.calories - avgCalories, 2), 0) / meals.length;
87
+ const calorieDistribution = 1 / (1 + calorieVariance / 10000);
88
+ const noConsecutiveRepeats = hasConsecutiveRepeats(meals) ? 0 : 1;
89
+ const overallScore = (diversity * 0.3) +
90
+ (categoryDiversity * 0.2) +
91
+ (proteinBalance * 0.2) +
92
+ (calorieDistribution * 0.15) +
93
+ (noConsecutiveRepeats * 0.15);
94
+ return {
95
+ diversity,
96
+ categoryDiversity,
97
+ proteinBalance,
98
+ calorieDistribution,
99
+ noConsecutiveRepeats,
100
+ overallScore
101
+ };
102
+ }
103
+ function selectDiverseMeals(availableRecipes, targetCalories, previousMeals = []) {
104
+ const shuffled = shuffleArray([...availableRecipes]);
105
+ const recentMealIds = new Set(previousMeals.slice(-3).map(m => m.id));
106
+ const prioritized = shuffled.sort((a, b) => {
107
+ const aRecent = recentMealIds.has(a.id) ? 1 : 0;
108
+ const bRecent = recentMealIds.has(b.id) ? 1 : 0;
109
+ return aRecent - bRecent;
110
+ });
111
+ let total = 0;
112
+ const selected = [];
113
+ const usedIds = new Set();
114
+ for (const recipe of prioritized) {
115
+ if (total >= targetCalories * 0.95)
116
+ break;
117
+ if (usedIds.has(recipe.id))
118
+ continue;
119
+ if (selected.length > 0 && selected[selected.length - 1].id === recipe.id) {
120
+ continue;
121
+ }
122
+ if (total + recipe.calories <= targetCalories * 1.05) {
123
+ selected.push(recipe);
124
+ usedIds.add(recipe.id);
125
+ total += recipe.calories;
126
+ }
127
+ }
128
+ return selected;
129
+ }
@@ -0,0 +1,3 @@
1
+ import { UserProfile } from './types/firestore';
2
+ import { MealPlan } from './types/firestore';
3
+ export declare function generateWeeklyMealPlan(profile: UserProfile, userId: string): MealPlan;
@@ -0,0 +1,468 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateWeeklyMealPlan = generateWeeklyMealPlan;
4
+ const foodConversion_1 = require("./foodConversion");
5
+ // Extended mock food data for weekly variety
6
+ const mockFoods = {
7
+ // Breakfast foods
8
+ oatmeal: {
9
+ id: 'oatmeal',
10
+ name: 'Oatmeal',
11
+ brand: 'Generic',
12
+ nutritionPer100g: { calories: 379, protein: 13.2, carbs: 66.3, fat: 6.9 },
13
+ servingSize: 40,
14
+ unit: 'g'
15
+ },
16
+ banana: {
17
+ id: 'banana',
18
+ name: 'Banana',
19
+ brand: 'Generic',
20
+ nutritionPer100g: { calories: 89, protein: 1.1, carbs: 22.8, fat: 0.3 },
21
+ servingSize: 118,
22
+ unit: 'g'
23
+ },
24
+ eggs: {
25
+ id: 'eggs',
26
+ name: 'Eggs',
27
+ brand: 'Generic',
28
+ nutritionPer100g: { calories: 155, protein: 13, carbs: 1.1, fat: 11 },
29
+ servingSize: 100,
30
+ unit: 'g'
31
+ },
32
+ yogurt: {
33
+ id: 'yogurt',
34
+ name: 'Greek Yogurt',
35
+ brand: 'Generic',
36
+ nutritionPer100g: { calories: 59, protein: 10, carbs: 3.6, fat: 0.4 },
37
+ servingSize: 170,
38
+ unit: 'g'
39
+ },
40
+ berries: {
41
+ id: 'berries',
42
+ name: 'Mixed Berries',
43
+ brand: 'Generic',
44
+ nutritionPer100g: { calories: 57, protein: 0.7, carbs: 14.5, fat: 0.3 },
45
+ servingSize: 100,
46
+ unit: 'g'
47
+ },
48
+ avocado_toast: {
49
+ id: 'avocado_toast',
50
+ name: 'Avocado Toast',
51
+ brand: 'Generic',
52
+ nutritionPer100g: { calories: 160, protein: 2, carbs: 9, fat: 15 },
53
+ servingSize: 150,
54
+ unit: 'g'
55
+ },
56
+ smoothie: {
57
+ id: 'smoothie',
58
+ name: 'Protein Smoothie',
59
+ brand: 'Generic',
60
+ nutritionPer100g: { calories: 150, protein: 20, carbs: 15, fat: 3 },
61
+ servingSize: 300,
62
+ unit: 'ml'
63
+ },
64
+ // Lunch proteins
65
+ chicken: {
66
+ id: 'chicken',
67
+ name: 'Chicken Breast',
68
+ brand: 'Generic',
69
+ nutritionPer100g: { calories: 165, protein: 31, carbs: 0, fat: 3.6 },
70
+ servingSize: 100,
71
+ unit: 'g'
72
+ },
73
+ salmon: {
74
+ id: 'salmon',
75
+ name: 'Salmon',
76
+ brand: 'Generic',
77
+ nutritionPer100g: { calories: 206, protein: 22, carbs: 0, fat: 13 },
78
+ servingSize: 100,
79
+ unit: 'g'
80
+ },
81
+ turkey: {
82
+ id: 'turkey',
83
+ name: 'Turkey Breast',
84
+ brand: 'Generic',
85
+ nutritionPer100g: { calories: 135, protein: 30, carbs: 0, fat: 1 },
86
+ servingSize: 100,
87
+ unit: 'g'
88
+ },
89
+ tuna: {
90
+ id: 'tuna',
91
+ name: 'Tuna',
92
+ brand: 'Generic',
93
+ nutritionPer100g: { calories: 144, protein: 30, carbs: 0, fat: 1 },
94
+ servingSize: 100,
95
+ unit: 'g'
96
+ },
97
+ tofu: {
98
+ id: 'tofu',
99
+ name: 'Tofu',
100
+ brand: 'Generic',
101
+ nutritionPer100g: { calories: 76, protein: 8, carbs: 1.9, fat: 4.8 },
102
+ servingSize: 150,
103
+ unit: 'g'
104
+ },
105
+ lentils: {
106
+ id: 'lentils',
107
+ name: 'Lentils',
108
+ brand: 'Generic',
109
+ nutritionPer100g: { calories: 116, protein: 9, carbs: 20, fat: 0.4 },
110
+ servingSize: 200,
111
+ unit: 'g'
112
+ },
113
+ quinoa: {
114
+ id: 'quinoa',
115
+ name: 'Quinoa',
116
+ brand: 'Generic',
117
+ nutritionPer100g: { calories: 120, protein: 4.4, carbs: 22, fat: 1.9 },
118
+ servingSize: 185,
119
+ unit: 'g'
120
+ },
121
+ // Carbs
122
+ rice: {
123
+ id: 'rice',
124
+ name: 'Brown Rice',
125
+ brand: 'Generic',
126
+ nutritionPer100g: { calories: 111, protein: 2.6, carbs: 23, fat: 0.9 },
127
+ servingSize: 185,
128
+ unit: 'g'
129
+ },
130
+ pasta: {
131
+ id: 'pasta',
132
+ name: 'Whole Wheat Pasta',
133
+ brand: 'Generic',
134
+ nutritionPer100g: { calories: 124, protein: 5, carbs: 25, fat: 1.1 },
135
+ servingSize: 140,
136
+ unit: 'g'
137
+ },
138
+ sweet_potato: {
139
+ id: 'sweet_potato',
140
+ name: 'Sweet Potato',
141
+ brand: 'Generic',
142
+ nutritionPer100g: { calories: 86, protein: 1.6, carbs: 20, fat: 0.1 },
143
+ servingSize: 200,
144
+ unit: 'g'
145
+ },
146
+ bread: {
147
+ id: 'bread',
148
+ name: 'Whole Wheat Bread',
149
+ brand: 'Generic',
150
+ nutritionPer100g: { calories: 247, protein: 13, carbs: 41, fat: 3.5 },
151
+ servingSize: 45,
152
+ unit: 'g'
153
+ },
154
+ // Vegetables
155
+ broccoli: {
156
+ id: 'broccoli',
157
+ name: 'Broccoli',
158
+ brand: 'Generic',
159
+ nutritionPer100g: { calories: 34, protein: 2.8, carbs: 7, fat: 0.4 },
160
+ servingSize: 91,
161
+ unit: 'g'
162
+ },
163
+ spinach: {
164
+ id: 'spinach',
165
+ name: 'Spinach',
166
+ brand: 'Generic',
167
+ nutritionPer100g: { calories: 23, protein: 2.9, carbs: 3.6, fat: 0.4 },
168
+ servingSize: 30,
169
+ unit: 'g'
170
+ },
171
+ carrots: {
172
+ id: 'carrots',
173
+ name: 'Carrots',
174
+ brand: 'Generic',
175
+ nutritionPer100g: { calories: 41, protein: 0.9, carbs: 9.6, fat: 0.2 },
176
+ servingSize: 100,
177
+ unit: 'g'
178
+ },
179
+ bell_peppers: {
180
+ id: 'bell_peppers',
181
+ name: 'Bell Peppers',
182
+ brand: 'Generic',
183
+ nutritionPer100g: { calories: 31, protein: 1, carbs: 7, fat: 0.3 },
184
+ servingSize: 120,
185
+ unit: 'g'
186
+ },
187
+ zucchini: {
188
+ id: 'zucchini',
189
+ name: 'Zucchini',
190
+ brand: 'Generic',
191
+ nutritionPer100g: { calories: 17, protein: 1.2, carbs: 3.1, fat: 0.3 },
192
+ servingSize: 200,
193
+ unit: 'g'
194
+ },
195
+ // Snacks
196
+ apple: {
197
+ id: 'apple',
198
+ name: 'Apple',
199
+ brand: 'Generic',
200
+ nutritionPer100g: { calories: 52, protein: 0.3, carbs: 13.8, fat: 0.2 },
201
+ servingSize: 182,
202
+ unit: 'g'
203
+ },
204
+ almonds: {
205
+ id: 'almonds',
206
+ name: 'Almonds',
207
+ brand: 'Generic',
208
+ nutritionPer100g: { calories: 579, protein: 21.2, carbs: 21.6, fat: 49.9 },
209
+ servingSize: 28,
210
+ unit: 'g'
211
+ },
212
+ walnuts: {
213
+ id: 'walnuts',
214
+ name: 'Walnuts',
215
+ brand: 'Generic',
216
+ nutritionPer100g: { calories: 654, protein: 15.2, carbs: 13.7, fat: 65.2 },
217
+ servingSize: 28,
218
+ unit: 'g'
219
+ },
220
+ orange: {
221
+ id: 'orange',
222
+ name: 'Orange',
223
+ brand: 'Generic',
224
+ nutritionPer100g: { calories: 47, protein: 0.9, carbs: 11.8, fat: 0.1 },
225
+ servingSize: 154,
226
+ unit: 'g'
227
+ }
228
+ };
229
+ function calculateDailyNeeds(profile) {
230
+ const age = profile.age || 30;
231
+ const height = profile.height || 170;
232
+ const weight = profile.weight || 70;
233
+ const gender = profile.gender || 'male';
234
+ const activityLevel = profile.activityLevel || 'moderately_active';
235
+ const bmr = gender === 'male'
236
+ ? 88.362 + (13.397 * weight) + (4.799 * height) - (5.677 * age)
237
+ : 447.593 + (9.247 * weight) + (3.098 * height) - (4.330 * age);
238
+ const activityMultipliers = {
239
+ sedentary: 1.2,
240
+ lightly_active: 1.375,
241
+ moderately_active: 1.55,
242
+ very_active: 1.725,
243
+ extremely_active: 1.9
244
+ };
245
+ const tdee = bmr * (activityMultipliers[activityLevel] || 1.2);
246
+ let dailyCalories = tdee;
247
+ if (profile.healthGoals?.includes('weight_loss')) {
248
+ dailyCalories -= 500;
249
+ }
250
+ else if (profile.healthGoals?.includes('weight_gain')) {
251
+ dailyCalories += 500;
252
+ }
253
+ return {
254
+ calories: Math.round(Math.max(dailyCalories, 1200)),
255
+ protein: Math.round(Math.max(dailyCalories * 0.3 / 4, 50)),
256
+ carbs: Math.round(Math.max(dailyCalories * 0.4 / 4, 100)),
257
+ fat: Math.round(Math.max(dailyCalories * 0.3 / 9, 30))
258
+ };
259
+ }
260
+ function createMeal(name, type, foods) {
261
+ const meal = (0, foodConversion_1.createMealFromFoodItems)(name, type, foods.map(({ food, amount, unit }, index) => ({
262
+ food: food,
263
+ amount,
264
+ unit
265
+ })));
266
+ return {
267
+ ...meal,
268
+ instructions: [`Prepare ${name} according to recipe`],
269
+ };
270
+ }
271
+ // Weekly meal plan templates
272
+ const weeklyMealTemplates = {
273
+ breakfast: [
274
+ { name: 'Oatmeal with Banana', foods: [
275
+ { food: mockFoods.oatmeal, amount: 50, unit: 'g' },
276
+ { food: mockFoods.banana, amount: 100, unit: 'g' }
277
+ ] },
278
+ { name: 'Greek Yogurt with Berries', foods: [
279
+ { food: mockFoods.yogurt, amount: 170, unit: 'g' },
280
+ { food: mockFoods.berries, amount: 100, unit: 'g' }
281
+ ] },
282
+ { name: 'Scrambled Eggs', foods: [
283
+ { food: mockFoods.eggs, amount: 100, unit: 'g' },
284
+ { food: mockFoods.spinach, amount: 50, unit: 'g' }
285
+ ] },
286
+ { name: 'Avocado Toast', foods: [
287
+ { food: mockFoods.avocado_toast, amount: 150, unit: 'g' }
288
+ ] },
289
+ { name: 'Protein Smoothie', foods: [
290
+ { food: mockFoods.smoothie, amount: 300, unit: 'ml' },
291
+ { food: mockFoods.berries, amount: 50, unit: 'g' }
292
+ ] },
293
+ { name: 'Oatmeal with Berries', foods: [
294
+ { food: mockFoods.oatmeal, amount: 50, unit: 'g' },
295
+ { food: mockFoods.berries, amount: 100, unit: 'g' }
296
+ ] },
297
+ { name: 'Yogurt Parfait', foods: [
298
+ { food: mockFoods.yogurt, amount: 170, unit: 'g' },
299
+ { food: mockFoods.banana, amount: 80, unit: 'g' },
300
+ { food: mockFoods.almonds, amount: 15, unit: 'g' }
301
+ ] }
302
+ ],
303
+ lunch: [
304
+ { name: 'Chicken Salad', foods: [
305
+ { food: mockFoods.chicken, amount: 150, unit: 'g' },
306
+ { food: mockFoods.spinach, amount: 100, unit: 'g' },
307
+ { food: mockFoods.carrots, amount: 80, unit: 'g' }
308
+ ] },
309
+ { name: 'Salmon with Quinoa', foods: [
310
+ { food: mockFoods.salmon, amount: 120, unit: 'g' },
311
+ { food: mockFoods.quinoa, amount: 150, unit: 'g' },
312
+ { food: mockFoods.broccoli, amount: 100, unit: 'g' }
313
+ ] },
314
+ { name: 'Turkey Wrap', foods: [
315
+ { food: mockFoods.turkey, amount: 100, unit: 'g' },
316
+ { food: mockFoods.bread, amount: 90, unit: 'g' },
317
+ { food: mockFoods.bell_peppers, amount: 80, unit: 'g' }
318
+ ] },
319
+ { name: 'Tuna Salad', foods: [
320
+ { food: mockFoods.tuna, amount: 100, unit: 'g' },
321
+ { food: mockFoods.spinach, amount: 100, unit: 'g' },
322
+ { food: mockFoods.sweet_potato, amount: 150, unit: 'g' }
323
+ ] },
324
+ { name: 'Tofu Stir Fry', foods: [
325
+ { food: mockFoods.tofu, amount: 150, unit: 'g' },
326
+ { food: mockFoods.bell_peppers, amount: 100, unit: 'g' },
327
+ { food: mockFoods.rice, amount: 150, unit: 'g' }
328
+ ] },
329
+ { name: 'Lentil Bowl', foods: [
330
+ { food: mockFoods.lentils, amount: 200, unit: 'g' },
331
+ { food: mockFoods.carrots, amount: 100, unit: 'g' },
332
+ { food: mockFoods.spinach, amount: 80, unit: 'g' }
333
+ ] },
334
+ { name: 'Chicken Pasta', foods: [
335
+ { food: mockFoods.chicken, amount: 120, unit: 'g' },
336
+ { food: mockFoods.pasta, amount: 140, unit: 'g' },
337
+ { food: mockFoods.zucchini, amount: 150, unit: 'g' }
338
+ ] }
339
+ ],
340
+ dinner: [
341
+ { name: 'Salmon with Sweet Potato', foods: [
342
+ { food: mockFoods.salmon, amount: 150, unit: 'g' },
343
+ { food: mockFoods.sweet_potato, amount: 200, unit: 'g' },
344
+ { food: mockFoods.broccoli, amount: 100, unit: 'g' }
345
+ ] },
346
+ { name: 'Chicken with Rice', foods: [
347
+ { food: mockFoods.chicken, amount: 150, unit: 'g' },
348
+ { food: mockFoods.rice, amount: 150, unit: 'g' },
349
+ { food: mockFoods.bell_peppers, amount: 120, unit: 'g' }
350
+ ] },
351
+ { name: 'Turkey with Quinoa', foods: [
352
+ { food: mockFoods.turkey, amount: 150, unit: 'g' },
353
+ { food: mockFoods.quinoa, amount: 150, unit: 'g' },
354
+ { food: mockFoods.zucchini, amount: 150, unit: 'g' }
355
+ ] },
356
+ { name: 'Tofu Curry', foods: [
357
+ { food: mockFoods.tofu, amount: 150, unit: 'g' },
358
+ { food: mockFoods.rice, amount: 150, unit: 'g' },
359
+ { food: mockFoods.spinach, amount: 100, unit: 'g' }
360
+ ] },
361
+ { name: 'Lentil Stew', foods: [
362
+ { food: mockFoods.lentils, amount: 200, unit: 'g' },
363
+ { food: mockFoods.carrots, amount: 100, unit: 'g' },
364
+ { food: mockFoods.sweet_potato, amount: 150, unit: 'g' }
365
+ ] },
366
+ { name: 'Tuna with Pasta', foods: [
367
+ { food: mockFoods.tuna, amount: 120, unit: 'g' },
368
+ { food: mockFoods.pasta, amount: 140, unit: 'g' },
369
+ { food: mockFoods.broccoli, amount: 100, unit: 'g' }
370
+ ] },
371
+ { name: 'Chicken Stir Fry', foods: [
372
+ { food: mockFoods.chicken, amount: 150, unit: 'g' },
373
+ { food: mockFoods.rice, amount: 150, unit: 'g' },
374
+ { food: mockFoods.bell_peppers, amount: 100, unit: 'g' },
375
+ { food: mockFoods.carrots, amount: 80, unit: 'g' }
376
+ ] }
377
+ ],
378
+ snack: [
379
+ { name: 'Apple with Almonds', foods: [
380
+ { food: mockFoods.apple, amount: 150, unit: 'g' },
381
+ { food: mockFoods.almonds, amount: 20, unit: 'g' }
382
+ ] },
383
+ { name: 'Greek Yogurt', foods: [
384
+ { food: mockFoods.yogurt, amount: 170, unit: 'g' }
385
+ ] },
386
+ { name: 'Orange with Walnuts', foods: [
387
+ { food: mockFoods.orange, amount: 150, unit: 'g' },
388
+ { food: mockFoods.walnuts, amount: 20, unit: 'g' }
389
+ ] },
390
+ { name: 'Berries', foods: [
391
+ { food: mockFoods.berries, amount: 150, unit: 'g' }
392
+ ] },
393
+ { name: 'Banana with Almonds', foods: [
394
+ { food: mockFoods.banana, amount: 118, unit: 'g' },
395
+ { food: mockFoods.almonds, amount: 15, unit: 'g' }
396
+ ] },
397
+ { name: 'Carrots with Almonds', foods: [
398
+ { food: mockFoods.carrots, amount: 100, unit: 'g' },
399
+ { food: mockFoods.almonds, amount: 15, unit: 'g' }
400
+ ] },
401
+ { name: 'Apple', foods: [
402
+ { food: mockFoods.apple, amount: 182, unit: 'g' }
403
+ ] }
404
+ ]
405
+ };
406
+ function generateWeeklyMealPlan(profile, userId) {
407
+ const needs = calculateDailyNeeds(profile);
408
+ const isVegetarian = profile.dietaryRestrictions?.includes('vegetarian');
409
+ const isVegan = profile.dietaryRestrictions?.includes('vegan');
410
+ const allMeals = [];
411
+ const daysOfWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
412
+ daysOfWeek.forEach((day, dayIndex) => {
413
+ // Filter meal templates based on dietary restrictions
414
+ const getFilteredMeals = (mealType) => {
415
+ return weeklyMealTemplates[mealType].filter(template => {
416
+ if (isVegan) {
417
+ return !template.foods.some(f => ['chicken', 'salmon', 'turkey', 'tuna', 'eggs', 'yogurt'].includes(f.food.id));
418
+ }
419
+ if (isVegetarian) {
420
+ return !template.foods.some(f => ['chicken', 'salmon', 'turkey', 'tuna'].includes(f.food.id));
421
+ }
422
+ return true;
423
+ });
424
+ };
425
+ // Select meals for each day with variety
426
+ const breakfastOptions = getFilteredMeals('breakfast');
427
+ const lunchOptions = getFilteredMeals('lunch');
428
+ const dinnerOptions = getFilteredMeals('dinner');
429
+ const snackOptions = getFilteredMeals('snack');
430
+ const breakfast = createMeal(`${day} - ${breakfastOptions[dayIndex % breakfastOptions.length].name}`, 'breakfast', breakfastOptions[dayIndex % breakfastOptions.length].foods);
431
+ const lunch = createMeal(`${day} - ${lunchOptions[dayIndex % lunchOptions.length].name}`, 'lunch', lunchOptions[dayIndex % lunchOptions.length].foods);
432
+ const dinner = createMeal(`${day} - ${dinnerOptions[dayIndex % dinnerOptions.length].name}`, 'dinner', dinnerOptions[dayIndex % dinnerOptions.length].foods);
433
+ const snack = createMeal(`${day} - ${snackOptions[dayIndex % snackOptions.length].name}`, 'snack', snackOptions[dayIndex % snackOptions.length].foods);
434
+ allMeals.push(breakfast, lunch, dinner, snack);
435
+ });
436
+ const totalNutrition = allMeals.reduce((total, meal) => ({
437
+ calories: total.calories + (meal.nutrition?.calories || 0),
438
+ protein: Math.round((total.protein + (meal.nutrition?.protein || 0)) * 10) / 10,
439
+ carbs: total.carbs + (meal.nutrition?.carbs || 0),
440
+ fat: total.fat + (meal.nutrition?.fat || 0)
441
+ }), { calories: 0, protein: 0, carbs: 0, fat: 0 });
442
+ const startDate = new Date();
443
+ const endDate = new Date();
444
+ endDate.setDate(startDate.getDate() + 6);
445
+ return {
446
+ id: `weekly-plan-${Date.now()}`,
447
+ userId: userId,
448
+ name: `Weekly Meal Plan - ${startDate.toLocaleDateString()} to ${endDate.toLocaleDateString()}`,
449
+ description: `Personalized 7-day meal plan with variety and balanced nutrition (${Math.round(totalNutrition.calories / 7)} cal/day avg)`,
450
+ startDate: startDate.toISOString().split('T')[0],
451
+ endDate: endDate.toISOString().split('T')[0],
452
+ dailyPlans: [],
453
+ meals: allMeals,
454
+ totalCalories: totalNutrition.calories,
455
+ totalProtein: totalNutrition.protein,
456
+ totalCarbs: totalNutrition.carbs,
457
+ totalFat: totalNutrition.fat,
458
+ uid: userId,
459
+ goals: {
460
+ dailyCalories: Math.round(totalNutrition.calories / 7),
461
+ proteinPercentage: 25,
462
+ carbsPercentage: 45,
463
+ fatPercentage: 30,
464
+ },
465
+ createdAt: new Date(),
466
+ updatedAt: new Date()
467
+ };
468
+ }
@@ -0,0 +1,12 @@
1
+ import { Recipe, UserProfile } from './types';
2
+ export interface DailyMealPlan {
3
+ date: string;
4
+ dailyCalories: number;
5
+ meals: Recipe[];
6
+ }
7
+ export interface WeeklyMealPlan {
8
+ weekCalories: number;
9
+ days: DailyMealPlan[];
10
+ diversityScore?: number;
11
+ }
12
+ export declare function generateWeeklyPlan(profile: UserProfile, recipes: Recipe[]): WeeklyMealPlan;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateWeeklyPlan = generateWeeklyPlan;
4
+ const bmr_1 = require("./bmr");
5
+ const rules_1 = require("./rules");
6
+ const variety_1 = require("./variety");
7
+ function generateWeeklyPlan(profile, recipes) {
8
+ const targetCalories = Math.round((0, bmr_1.calculateTDEE)(profile));
9
+ const filtered = (0, rules_1.applyHealthRules)(recipes, profile.conditions);
10
+ const days = [];
11
+ let weekTotal = 0;
12
+ let allWeekMeals = [];
13
+ const daysOfWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
14
+ for (let i = 0; i < 7; i++) {
15
+ const dailyMeals = (0, variety_1.selectDiverseMeals)(filtered, targetCalories, allWeekMeals);
16
+ const dailyTotal = dailyMeals.reduce((sum, m) => sum + m.calories, 0);
17
+ days.push({
18
+ date: daysOfWeek[i],
19
+ dailyCalories: dailyTotal,
20
+ meals: dailyMeals
21
+ });
22
+ weekTotal += dailyTotal;
23
+ allWeekMeals.push(...dailyMeals);
24
+ }
25
+ const weekScore = (0, variety_1.scoreMealPlan)(allWeekMeals);
26
+ return {
27
+ weekCalories: weekTotal,
28
+ days,
29
+ diversityScore: weekScore.overallScore
30
+ };
31
+ }