hevy-shared 1.0.911 → 1.0.913

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.
@@ -142,6 +142,13 @@ export declare const normalizeExerciseCategory: (exercise: {
142
142
  is_custom: boolean;
143
143
  category?: ExerciseCategory;
144
144
  }) => HevyTrainerExerciseCategory;
145
+ /**
146
+ * Checks if an exercise is compatible with the user's selected granular equipments
147
+ *
148
+ * - If the exercise has no granular equipments, it is compatible no matter what the user has selected
149
+ * - Otherwise, the exercise is only allowed if the user has all the granular equipments
150
+ */
151
+ export declare const isEquipmentCompatible: (exercise: HevyTrainerLibraryExercise, userSelectedGranularEquipments: GranularEquipment[]) => boolean;
145
152
  /**
146
153
  * Sorts exercises by priority for each muscle group based on the provided priorities
147
154
  * and adds any remaining exercises from the store that weren't in the priorities.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateProgram = exports.pickExerciseForPrescription = exports.getPrioritySortedExercises = exports.normalizeExerciseCategory = exports.getTrainerRestTimerSeconds = exports.getTrainerRepRange = exports.getTrainerSetCount = exports.programSplits = exports.frequencyMap = exports.routineNames = exports.defaultDurationPerFrequency = exports.hevyTrainerExerciseCategories = exports.trainerEquipmentToGranularEquipments = exports.granularEquipmentDefaults = exports.trainerGymTypes = exports.workoutDurationOptions = void 0;
3
+ exports.generateProgram = exports.pickExerciseForPrescription = exports.getPrioritySortedExercises = exports.isEquipmentCompatible = exports.normalizeExerciseCategory = exports.getTrainerRestTimerSeconds = exports.getTrainerRepRange = exports.getTrainerSetCount = exports.programSplits = exports.frequencyMap = exports.routineNames = exports.defaultDurationPerFrequency = exports.hevyTrainerExerciseCategories = exports.trainerEquipmentToGranularEquipments = exports.granularEquipmentDefaults = exports.trainerGymTypes = exports.workoutDurationOptions = void 0;
4
4
  const _1 = require(".");
5
5
  const MAX_BARBELL_EXERCISES_PER_ROUTINE = 2;
6
6
  exports.workoutDurationOptions = [40, 60, 80];
@@ -80,53 +80,27 @@ const trainerEquipmentToGranularEquipmentsMap = {
80
80
  barbell: [
81
81
  'adjustable_bench',
82
82
  'barbell',
83
- 'dip_bar',
84
83
  'ez_bar',
85
84
  'flat_bench',
86
85
  'landmine',
87
- 'medicine_ball',
88
86
  'plate',
89
- 'pullup_bar',
90
- 'rings',
91
- 'rope',
92
87
  'squat_rack',
93
88
  't_bar',
94
- 'trap_bar',
95
- ],
96
- dumbbell: [
97
- 'adjustable_bench',
98
- 'dip_bar',
99
- 'dumbbell',
100
- 'flat_bench',
101
- 'medicine_ball',
102
- 'pullup_bar',
103
- 'rings',
104
- 'rope',
105
89
  ],
90
+ dumbbell: ['adjustable_bench', 'dumbbell', 'flat_bench'],
106
91
  machine: [
107
92
  'adjustable_bench',
108
- 'air_bike',
109
- 'battle_rope',
110
93
  'dip_bar',
111
94
  'dual_cable_machine',
112
- 'elliptical_trainer',
113
95
  'flat_bench',
114
96
  'lat_pulldown_cable',
115
97
  'leg_press_machine',
116
- 'medicine_ball',
117
98
  'plate',
118
99
  'plate_machines',
119
100
  'pullup_bar',
120
- 'resistance_band',
121
- 'rings',
122
- 'rope',
123
- 'rowing_machine',
124
101
  'single_cable_machine',
125
102
  'smith_machine',
126
- 'spinning',
127
103
  'stack_machines',
128
- 'stair_machine',
129
- 'treadmill',
130
104
  ],
131
105
  };
132
106
  const trainerEquipmentToGranularEquipments = (equipments) => {
@@ -263,8 +237,18 @@ const isEquipmentCompatible = (exercise, userSelectedGranularEquipments) => {
263
237
  exercise.granular_equipments.length === 0) {
264
238
  return true;
265
239
  }
266
- return exercise.granular_equipments.every((granularEquipment) => userSelectedGranularEquipments.includes(granularEquipment));
240
+ const allowedEquipments = [...userSelectedGranularEquipments];
241
+ // Special case: adjustable bench can substitute for flat bench
242
+ if (userSelectedGranularEquipments.includes('adjustable_bench')) {
243
+ allowedEquipments.push('flat_bench');
244
+ }
245
+ // Special case: dual cable machine can substitute for single cable machine
246
+ if (userSelectedGranularEquipments.includes('dual_cable_machine')) {
247
+ allowedEquipments.push('single_cable_machine');
248
+ }
249
+ return exercise.granular_equipments.every((granularEquipment) => allowedEquipments.includes(granularEquipment));
267
250
  };
251
+ exports.isEquipmentCompatible = isEquipmentCompatible;
268
252
  /**
269
253
  * Checks if the routine barbell exercise limit is exceeded
270
254
  *
@@ -364,7 +348,7 @@ const isExerciseMatch = (exercise, criteria) => {
364
348
  criteria.exerciseCategory === 'compound');
365
349
  const levelMatch = (_b = (_a = exercise.level) === null || _a === void 0 ? void 0 : _a.includes(criteria.level)) !== null && _b !== void 0 ? _b : false;
366
350
  const goalMatch = (_d = (_c = exercise.goal) === null || _c === void 0 ? void 0 : _c.includes(criteria.goal)) !== null && _d !== void 0 ? _d : false;
367
- const equipmentMatch = isEquipmentCompatible(exercise, criteria.granularEquipments);
351
+ const equipmentMatch = (0, exports.isEquipmentCompatible)(exercise, criteria.granularEquipments);
368
352
  const barbellLimitOk = !isBarbellLimitExceeded(exercise, criteria.routineBarbellExerciseCount, criteria.granularEquipments);
369
353
  return (categoryMatch && levelMatch && goalMatch && equipmentMatch && barbellLimitOk);
370
354
  };
@@ -375,7 +359,7 @@ const isAlternativeIsolationExerciseMatch = (exercise, criteria) => {
375
359
  var _a, _b;
376
360
  const isIsolationCategory = criteria.exerciseCategory === 'isolation';
377
361
  const isAlternativeExercise = isolationExerciseAlternatives[criteria.muscleGroup].includes(exercise.id);
378
- const equipmentMatch = isEquipmentCompatible(exercise, criteria.granularEquipments);
362
+ const equipmentMatch = (0, exports.isEquipmentCompatible)(exercise, criteria.granularEquipments);
379
363
  const levelMatch = (_b = (_a = exercise.level) === null || _a === void 0 ? void 0 : _a.includes(criteria.level)) !== null && _b !== void 0 ? _b : false;
380
364
  return (isIsolationCategory && isAlternativeExercise && equipmentMatch && levelMatch);
381
365
  };
package/built/index.d.ts CHANGED
@@ -1270,7 +1270,7 @@ export interface PostHevyTrainerProgramRequestBody {
1270
1270
  level: TrainingLevel;
1271
1271
  goal: TrainingGoal;
1272
1272
  /** @deprecated Use granular_equipments instead */
1273
- equipments: HevyTrainerProgramEquipment[];
1273
+ equipments?: HevyTrainerProgramEquipment[];
1274
1274
  granular_equipments: GranularEquipment[];
1275
1275
  weekly_frequency: WeeklyTrainingFrequency;
1276
1276
  focus_muscle?: SimplifiedMuscleGroup;
@@ -1286,7 +1286,7 @@ export interface UpdateHevyTrainerProgramRequestBody {
1286
1286
  level: TrainingLevel;
1287
1287
  goal: TrainingGoal;
1288
1288
  /** @deprecated Use granular_equipments instead */
1289
- equipments: HevyTrainerProgramEquipment[];
1289
+ equipments?: HevyTrainerProgramEquipment[];
1290
1290
  granular_equipments: GranularEquipment[];
1291
1291
  weekly_frequency: WeeklyTrainingFrequency;
1292
1292
  focus_muscle?: SimplifiedMuscleGroup;
@@ -11,59 +11,33 @@ describe('trainerEquipmentToGranularEquipments', () => {
11
11
  expect(sortGranular(result)).toEqual(sortGranular([
12
12
  'adjustable_bench',
13
13
  'barbell',
14
- 'dip_bar',
15
14
  'ez_bar',
16
15
  'flat_bench',
17
16
  'landmine',
18
- 'medicine_ball',
19
17
  'plate',
20
- 'pullup_bar',
21
- 'rings',
22
- 'rope',
23
18
  'squat_rack',
24
19
  't_bar',
25
- 'trap_bar',
26
20
  ]));
27
21
  });
28
22
  it('returns expected granular equipments for dumbbell only', () => {
29
23
  const result = (0, hevyTrainer_1.trainerEquipmentToGranularEquipments)(['dumbbell']);
30
- expect(sortGranular(result)).toEqual(sortGranular([
31
- 'adjustable_bench',
32
- 'dip_bar',
33
- 'dumbbell',
34
- 'flat_bench',
35
- 'medicine_ball',
36
- 'pullup_bar',
37
- 'rings',
38
- 'rope',
39
- ]));
24
+ expect(sortGranular(result)).toEqual(sortGranular(['adjustable_bench', 'dumbbell', 'flat_bench']));
40
25
  });
41
26
  it('returns expected granular equipments for machine only', () => {
42
27
  const result = (0, hevyTrainer_1.trainerEquipmentToGranularEquipments)(['machine']);
43
28
  expect(sortGranular(result)).toEqual(sortGranular([
44
29
  'adjustable_bench',
45
- 'air_bike',
46
- 'battle_rope',
47
30
  'dip_bar',
48
31
  'dual_cable_machine',
49
- 'elliptical_trainer',
50
32
  'flat_bench',
51
33
  'lat_pulldown_cable',
52
34
  'leg_press_machine',
53
- 'medicine_ball',
54
35
  'plate',
55
36
  'plate_machines',
56
37
  'pullup_bar',
57
- 'resistance_band',
58
- 'rings',
59
- 'rope',
60
- 'rowing_machine',
61
38
  'single_cable_machine',
62
39
  'smith_machine',
63
- 'spinning',
64
40
  'stack_machines',
65
- 'stair_machine',
66
- 'treadmill',
67
41
  ]));
68
42
  });
69
43
  it('returns deduped union for multiple equipments', () => {
@@ -75,6 +49,53 @@ describe('trainerEquipmentToGranularEquipments', () => {
75
49
  expect(sortGranular(result)).toEqual(combined);
76
50
  });
77
51
  });
52
+ const createExercise = (granularEquipments) => ({
53
+ id: 'test-exercise',
54
+ title: 'Test Exercise',
55
+ granular_equipments: granularEquipments,
56
+ });
57
+ describe('isEquipmentCompatible', () => {
58
+ it('returns true when exercise has no granular equipments', () => {
59
+ const exercise = createExercise(undefined);
60
+ expect((0, hevyTrainer_1.isEquipmentCompatible)(exercise, ['barbell'])).toBe(true);
61
+ });
62
+ it('returns true when exercise has empty granular equipments', () => {
63
+ const exercise = createExercise([]);
64
+ expect((0, hevyTrainer_1.isEquipmentCompatible)(exercise, ['barbell'])).toBe(true);
65
+ });
66
+ it('returns true when user has all required equipments', () => {
67
+ const exercise = createExercise(['barbell', 'squat_rack']);
68
+ expect((0, hevyTrainer_1.isEquipmentCompatible)(exercise, ['barbell', 'squat_rack'])).toBe(true);
69
+ });
70
+ it('returns false when user is missing required equipment', () => {
71
+ const exercise = createExercise(['barbell', 'squat_rack']);
72
+ expect((0, hevyTrainer_1.isEquipmentCompatible)(exercise, ['barbell'])).toBe(false);
73
+ });
74
+ it('returns true when user has more equipment than required', () => {
75
+ const exercise = createExercise(['barbell']);
76
+ expect((0, hevyTrainer_1.isEquipmentCompatible)(exercise, ['barbell', 'squat_rack', 'dumbbell'])).toBe(true);
77
+ });
78
+ describe('special case: adjustable_bench / flat_bench substitution', () => {
79
+ it('allows adjustable_bench to substitute for flat_bench when exercise requires flat_bench', () => {
80
+ const exercise = createExercise(['flat_bench']);
81
+ expect((0, hevyTrainer_1.isEquipmentCompatible)(exercise, ['adjustable_bench'])).toBe(true);
82
+ });
83
+ it('does NOT allow flat_bench to substitute for adjustable_bench when exercise requires adjustable_bench', () => {
84
+ const exercise = createExercise(['adjustable_bench']);
85
+ expect((0, hevyTrainer_1.isEquipmentCompatible)(exercise, ['flat_bench'])).toBe(false);
86
+ });
87
+ });
88
+ describe('special case: dual_cable_machine / single_cable_machine substitution', () => {
89
+ it('allows dual_cable_machine to substitute for single_cable_machine when exercise requires single_cable_machine', () => {
90
+ const exercise = createExercise(['single_cable_machine']);
91
+ expect((0, hevyTrainer_1.isEquipmentCompatible)(exercise, ['dual_cable_machine'])).toBe(true);
92
+ });
93
+ it('does NOT allow single_cable_machine to substitute for dual_cable_machine when exercise requires dual_cable_machine', () => {
94
+ const exercise = createExercise(['dual_cable_machine']);
95
+ expect((0, hevyTrainer_1.isEquipmentCompatible)(exercise, ['single_cable_machine'])).toBe(false);
96
+ });
97
+ });
98
+ });
78
99
  const __1 = require("..");
79
100
  describe('hevyTrainer gym types and defaults', () => {
80
101
  it('defines granular equipment defaults for every trainer gym type', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hevy-shared",
3
- "version": "1.0.911",
3
+ "version": "1.0.913",
4
4
  "description": "",
5
5
  "main": "built/index.js",
6
6
  "types": "built/index.d.ts",