hevy-shared 1.0.1018 → 1.0.1020
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/built/hevyTrainer.d.ts +54 -45
- package/built/hevyTrainer.js +65 -64
- package/built/index.d.ts +11 -333
- package/built/index.js +3 -8
- package/built/routineUtils.d.ts +1 -2
- package/built/tests/hevyTrainer.test.js +75 -62
- package/built/tests/testUtils.d.ts +1 -2
- package/built/tests/testUtils.js +1 -2
- package/built/workout/index.d.ts +11 -0
- package/built/workout/index.js +20 -0
- package/built/{normalizedWorkoutUtils.d.ts → workout/normalizedWorkout.d.ts} +2 -3
- package/built/{normalizedWorkoutUtils.js → workout/normalizedWorkout.js} +2 -2
- package/built/workout/postWorkoutRequest.d.ts +77 -0
- package/built/workout/postWorkoutRequest.js +2 -0
- package/built/workout/publicWorkout.d.ts +93 -0
- package/built/workout/publicWorkout.js +7 -0
- package/built/workout/userWorkout.d.ts +130 -0
- package/built/workout/userWorkout.js +2 -0
- package/built/workout/workout.d.ts +120 -0
- package/built/workout/workout.js +2 -0
- package/package.json +3 -5
package/built/hevyTrainer.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { WeeklyTrainingFrequency, TrainingGoal, TrainingLevel, SimplifiedMuscleGroup, MuscleGroup, LibraryExercise, ExerciseCategory, GranularEquipment, HevyTrainerProgramEquipment, RestTimerLength, CardioPreference
|
|
1
|
+
import { WeeklyTrainingFrequency, TrainingGoal, TrainingLevel, SimplifiedMuscleGroup, MuscleGroup, LibraryExercise, ExerciseCategory, GranularEquipment, HevyTrainerProgramEquipment, RestTimerLength, CardioPreference } from '.';
|
|
2
2
|
export type HevyTrainerExerciseCategory = typeof hevyTrainerExerciseCategories[number];
|
|
3
|
-
export type
|
|
3
|
+
export type HevyTrainerRoutineName = typeof routineNames[number];
|
|
4
4
|
export declare const workoutDurationOptions: readonly [40, 60, 80];
|
|
5
5
|
export type WorkoutDurationMinutes = typeof workoutDurationOptions[number];
|
|
6
6
|
export declare const trainerGymTypes: readonly ["home_gym", "garage_gym", "commercial_gym", "full_gym"];
|
|
@@ -16,12 +16,12 @@ export declare const trainerEquipmentToGranularEquipments: (equipments: HevyTrai
|
|
|
16
16
|
export declare const granularEquipmentsToTrainerEquipments: (granularEquipments: GranularEquipment[]) => HevyTrainerProgramEquipment[];
|
|
17
17
|
export declare const hevyTrainerExerciseCategories: readonly ["compound", "isolation"];
|
|
18
18
|
export declare const defaultDurationPerFrequency: Record<WeeklyTrainingFrequency, WorkoutDurationMinutes>;
|
|
19
|
-
export declare const
|
|
19
|
+
export declare const routineNames: readonly ["full_body_1", "full_body_2_a", "full_body_2_b", "full_body_3_a", "full_body_3_b", "full_body_3_c", "upper_1_a", "lower_1_a", "upper_1_b", "lower_1_b", "push_1", "pull_1", "legs_1", "upper_2", "lower_2", "push_2_a", "pull_2_a", "legs_2_a", "push_2_b", "pull_2_b", "legs_2_b"];
|
|
20
20
|
export type exerciseId = string;
|
|
21
21
|
export interface ExerciseSelectionCriteria {
|
|
22
22
|
exerciseCategory: HevyTrainerExerciseCategory | 'all';
|
|
23
23
|
equipments: GranularEquipment[];
|
|
24
|
-
|
|
24
|
+
routineBarbellExerciseCount: number;
|
|
25
25
|
level: TrainingLevel;
|
|
26
26
|
goal: TrainingGoal;
|
|
27
27
|
muscleGroup: MuscleGroup;
|
|
@@ -29,7 +29,7 @@ export interface ExerciseSelectionCriteria {
|
|
|
29
29
|
}
|
|
30
30
|
export interface ExerciseSelectionContext {
|
|
31
31
|
programUsedExerciseIds?: Set<string>;
|
|
32
|
-
|
|
32
|
+
routineUsedExerciseIds?: Set<string>;
|
|
33
33
|
excludedExerciseIds?: Set<string>;
|
|
34
34
|
}
|
|
35
35
|
export interface ExerciseSelectionParams<T extends HevyTrainerLibraryExercise> {
|
|
@@ -69,7 +69,7 @@ export interface ProgramGenerationParams<T extends HevyTrainerLibraryExercise> {
|
|
|
69
69
|
}
|
|
70
70
|
export interface TemplateExerciseSelectionTraceRecord {
|
|
71
71
|
traceKind: 'template';
|
|
72
|
-
|
|
72
|
+
routine: HevyTrainerRoutineName;
|
|
73
73
|
prescriptionIndex: number;
|
|
74
74
|
prescription: ExercisePrescription;
|
|
75
75
|
resolvedMuscleGroup: MuscleGroup;
|
|
@@ -79,7 +79,7 @@ export interface TemplateExerciseSelectionTraceRecord {
|
|
|
79
79
|
}
|
|
80
80
|
export interface CardioExerciseSelectionTraceRecord {
|
|
81
81
|
traceKind: 'cardio';
|
|
82
|
-
|
|
82
|
+
routine: HevyTrainerRoutineName;
|
|
83
83
|
prescriptionIndex: number;
|
|
84
84
|
selectedExerciseId?: string;
|
|
85
85
|
equipments: GranularEquipment[];
|
|
@@ -87,24 +87,24 @@ export interface CardioExerciseSelectionTraceRecord {
|
|
|
87
87
|
trace: ExerciseSelectionTrace;
|
|
88
88
|
}
|
|
89
89
|
export type ProgramExerciseSelectionTraceRecord = TemplateExerciseSelectionTraceRecord | CardioExerciseSelectionTraceRecord;
|
|
90
|
-
export type TrainerProgramAttemptWithTraces
|
|
90
|
+
export type TrainerProgramAttemptWithTraces = TrainerProgramAttempt & {
|
|
91
91
|
exerciseSelectionTraces: ProgramExerciseSelectionTraceRecord[];
|
|
92
92
|
};
|
|
93
93
|
export type FrequencyString = 'one_day' | 'two_days' | 'three_days' | 'four_days' | 'five_days' | 'six_days';
|
|
94
94
|
export declare const frequencyMap: Record<WeeklyTrainingFrequency, FrequencyString>;
|
|
95
|
-
export declare const programSplits: Record<WeeklyTrainingFrequency,
|
|
95
|
+
export declare const programSplits: Record<WeeklyTrainingFrequency, HevyTrainerRoutineName[]>;
|
|
96
96
|
export type SetsPerGoal = {
|
|
97
97
|
[key in TrainingGoal]: number;
|
|
98
98
|
};
|
|
99
99
|
export type SetsPerFrequency = {
|
|
100
100
|
[key in FrequencyString]: SetsPerGoal;
|
|
101
101
|
};
|
|
102
|
-
export interface
|
|
102
|
+
export interface RepRange {
|
|
103
103
|
rep_range_start: number;
|
|
104
104
|
rep_range_end: number;
|
|
105
105
|
}
|
|
106
106
|
export type RepRangesPerCategory = {
|
|
107
|
-
[key in HevyTrainerExerciseCategory]:
|
|
107
|
+
[key in HevyTrainerExerciseCategory]: RepRange;
|
|
108
108
|
};
|
|
109
109
|
export type RepRanges = {
|
|
110
110
|
[key in TrainingGoal]: RepRangesPerCategory;
|
|
@@ -135,11 +135,10 @@ export type ExerciseReplacements = {
|
|
|
135
135
|
};
|
|
136
136
|
export interface WorkoutTemplate {
|
|
137
137
|
exercises: ExercisePrescription[];
|
|
138
|
-
/** @deprecated Because this is english-only and not translatable so we are not using it currently */
|
|
139
138
|
notes?: string;
|
|
140
139
|
}
|
|
141
140
|
export type Templates = {
|
|
142
|
-
[key in
|
|
141
|
+
[key in HevyTrainerRoutineName]: WorkoutTemplate;
|
|
143
142
|
};
|
|
144
143
|
export interface BackofficeTrainerPreset {
|
|
145
144
|
id?: number;
|
|
@@ -158,38 +157,39 @@ export interface TrainerAlgorithmSettings {
|
|
|
158
157
|
exercise_notes: ExerciseNotes;
|
|
159
158
|
exercise_replacements: ExerciseReplacements;
|
|
160
159
|
}
|
|
161
|
-
|
|
160
|
+
/** Resistance prescriptions from templates (sets, reps, rest, notes). */
|
|
161
|
+
export interface TrainerProgramResistanceExercise {
|
|
162
162
|
kind: 'resistance';
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
163
|
+
exerciseTemplate: HevyTrainerLibraryExercise;
|
|
164
|
+
muscleGroup: MuscleGroup | 'focus_muscle';
|
|
165
|
+
category: HevyTrainerExerciseCategory;
|
|
166
|
+
sets: number;
|
|
167
|
+
warmupSetCount?: number;
|
|
168
|
+
repRangeStart: number;
|
|
169
|
+
repRangeEnd: number;
|
|
170
|
+
restTimerSeconds: number;
|
|
171
|
+
notes?: string;
|
|
168
172
|
}
|
|
169
|
-
|
|
173
|
+
/** Optional cardio attachment — no prescription semantics beyond exercise choice. */
|
|
174
|
+
export interface TrainerProgramCardioExercise {
|
|
170
175
|
kind: 'cardio';
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
duration_seconds: number;
|
|
176
|
+
exerciseTemplate: HevyTrainerLibraryExercise;
|
|
177
|
+
durationSeconds: number;
|
|
174
178
|
}
|
|
175
|
-
export
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
export declare const isTrainerProgramCardioExercise: <T>(exercise: TrainerProgramExercise<T>) => exercise is TrainerProgramCardioExercise<T>;
|
|
183
|
-
export declare const isTrainerProgramOtherExercise: <T>(exercise: TrainerProgramExercise<T>) => exercise is TrainerProgramOtherExercise<T>;
|
|
184
|
-
export interface TrainerProgramWorkoutTemplate<T> {
|
|
185
|
-
name: TrainerWorkoutTemplateName;
|
|
186
|
-
exercises: TrainerProgramExercise<T>[];
|
|
179
|
+
export type TrainerProgramExercise = TrainerProgramResistanceExercise | TrainerProgramCardioExercise;
|
|
180
|
+
export declare const isTrainerProgramResistanceExercise: (exercise: TrainerProgramExercise) => exercise is TrainerProgramResistanceExercise;
|
|
181
|
+
export declare const isTrainerProgramCardioExercise: (exercise: TrainerProgramExercise) => exercise is TrainerProgramCardioExercise;
|
|
182
|
+
export interface TrainerProgramRoutine {
|
|
183
|
+
name: HevyTrainerRoutineName;
|
|
184
|
+
exercises: TrainerProgramExercise[];
|
|
185
|
+
notes?: string;
|
|
187
186
|
}
|
|
188
|
-
export interface
|
|
189
|
-
|
|
187
|
+
export interface TrainerProgram {
|
|
188
|
+
name: WeeklyTrainingFrequency;
|
|
189
|
+
routines: TrainerProgramRoutine[];
|
|
190
190
|
}
|
|
191
191
|
export declare const getTrainerSetCount: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, frequency: WeeklyTrainingFrequency) => number;
|
|
192
|
-
export declare const getTrainerRepRange: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, exerciseCategory: HevyTrainerExerciseCategory) =>
|
|
192
|
+
export declare const getTrainerRepRange: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, exerciseCategory: HevyTrainerExerciseCategory) => RepRange;
|
|
193
193
|
export declare const getTrainerRestTimerSeconds: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, length: RestTimerLength, exerciseCategory: HevyTrainerExerciseCategory) => number;
|
|
194
194
|
/**
|
|
195
195
|
* Normalizes the exercise category to a HevyTrainerExerciseCategory
|
|
@@ -243,16 +243,25 @@ export interface ExercisePrescriptionError {
|
|
|
243
243
|
focusMuscle?: SimplifiedMuscleGroup;
|
|
244
244
|
};
|
|
245
245
|
}
|
|
246
|
-
export interface
|
|
246
|
+
export interface TrainerProgramExerciseResult {
|
|
247
|
+
success: true;
|
|
248
|
+
exercise: TrainerProgramExercise;
|
|
249
|
+
}
|
|
250
|
+
export interface TrainerProgramExerciseError {
|
|
251
|
+
success: false;
|
|
252
|
+
error: ExercisePrescriptionError;
|
|
253
|
+
}
|
|
254
|
+
export type TrainerProgramExerciseAttempt = TrainerProgramExerciseResult | TrainerProgramExerciseError;
|
|
255
|
+
export interface TrainerProgramResult {
|
|
247
256
|
success: true;
|
|
248
|
-
program:
|
|
257
|
+
program: TrainerProgram;
|
|
249
258
|
}
|
|
250
|
-
export interface TrainerProgramError
|
|
259
|
+
export interface TrainerProgramError {
|
|
251
260
|
success: false;
|
|
252
261
|
errors: ExercisePrescriptionError[];
|
|
253
|
-
partialProgram:
|
|
262
|
+
partialProgram: TrainerProgram;
|
|
254
263
|
}
|
|
255
|
-
export type TrainerProgramAttempt
|
|
264
|
+
export type TrainerProgramAttempt = TrainerProgramResult | TrainerProgramError;
|
|
256
265
|
/**
|
|
257
266
|
* Generates a complete training program based on the provided parameters
|
|
258
267
|
* - debugExerciseSelectionTrace: true -> includes exercise selection trace breadcrumbs in the result for debugging.
|
|
@@ -260,6 +269,6 @@ export type TrainerProgramAttempt<T> = TrainerProgramResult<T> | TrainerProgramE
|
|
|
260
269
|
*/
|
|
261
270
|
export declare function generateProgram<T extends HevyTrainerLibraryExercise>(params: ProgramGenerationParams<T> & {
|
|
262
271
|
debugExerciseSelectionTrace: true;
|
|
263
|
-
}): TrainerProgramAttemptWithTraces
|
|
264
|
-
export declare function generateProgram<T extends HevyTrainerLibraryExercise>(params: ProgramGenerationParams<T>): TrainerProgramAttempt
|
|
272
|
+
}): TrainerProgramAttemptWithTraces;
|
|
273
|
+
export declare function generateProgram<T extends HevyTrainerLibraryExercise>(params: ProgramGenerationParams<T>): TrainerProgramAttempt;
|
|
265
274
|
export {};
|
package/built/hevyTrainer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = exports.getPrioritySortedExercises = exports.isEquipmentCompatible = exports.normalizeExerciseCategory = exports.getTrainerRestTimerSeconds = exports.getTrainerRepRange = exports.getTrainerSetCount = exports.
|
|
3
|
+
exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = exports.getPrioritySortedExercises = exports.isEquipmentCompatible = exports.normalizeExerciseCategory = exports.getTrainerRestTimerSeconds = exports.getTrainerRepRange = exports.getTrainerSetCount = exports.isTrainerProgramCardioExercise = exports.isTrainerProgramResistanceExercise = exports.programSplits = exports.frequencyMap = exports.routineNames = exports.defaultDurationPerFrequency = exports.hevyTrainerExerciseCategories = exports.granularEquipmentsToTrainerEquipments = exports.trainerEquipmentToGranularEquipments = exports.granularEquipmentDefaults = exports.trainerGymTypes = exports.workoutDurationOptions = void 0;
|
|
4
4
|
exports.pickTrainerExercise = pickTrainerExercise;
|
|
5
5
|
exports.generateProgram = generateProgram;
|
|
6
6
|
const _1 = require(".");
|
|
@@ -152,7 +152,7 @@ exports.defaultDurationPerFrequency = {
|
|
|
152
152
|
5: 40,
|
|
153
153
|
6: 40,
|
|
154
154
|
};
|
|
155
|
-
exports.
|
|
155
|
+
exports.routineNames = [
|
|
156
156
|
// Full body 1x
|
|
157
157
|
'full_body_1',
|
|
158
158
|
// Full body 2x
|
|
@@ -201,8 +201,6 @@ const isTrainerProgramResistanceExercise = (exercise) => exercise.kind === 'resi
|
|
|
201
201
|
exports.isTrainerProgramResistanceExercise = isTrainerProgramResistanceExercise;
|
|
202
202
|
const isTrainerProgramCardioExercise = (exercise) => exercise.kind === 'cardio';
|
|
203
203
|
exports.isTrainerProgramCardioExercise = isTrainerProgramCardioExercise;
|
|
204
|
-
const isTrainerProgramOtherExercise = (exercise) => exercise.kind === 'other';
|
|
205
|
-
exports.isTrainerProgramOtherExercise = isTrainerProgramOtherExercise;
|
|
206
204
|
const getTrainerSetCount = (trainerAlgorithmSettings, goal, frequency) => {
|
|
207
205
|
return trainerAlgorithmSettings.sets[exports.frequencyMap[frequency]][goal];
|
|
208
206
|
};
|
|
@@ -293,12 +291,12 @@ const MAX_BARBELL_EXERCISES_FOR_ONCE_PER_WEEK = 3;
|
|
|
293
291
|
* - Only enforce the cap when the user has at least one "barbell substitute" equipment available
|
|
294
292
|
* (otherwise we allow barbell exercises to avoid running out of viable options)
|
|
295
293
|
*/
|
|
296
|
-
const isBarbellExerciseAllowed = (exercise,
|
|
294
|
+
const isBarbellExerciseAllowed = (exercise, routineBarbellExerciseCount, userEquipments, frequency) => {
|
|
297
295
|
const isCandidateBarbell = exercise.equipment_category === 'barbell';
|
|
298
296
|
const isOncePerWeek = frequency === 1;
|
|
299
297
|
if (!isCandidateBarbell || !isOncePerWeek)
|
|
300
298
|
return true;
|
|
301
|
-
const isAtOrOverLimit =
|
|
299
|
+
const isAtOrOverLimit = routineBarbellExerciseCount >= MAX_BARBELL_EXERCISES_FOR_ONCE_PER_WEEK;
|
|
302
300
|
if (!isAtOrOverLimit)
|
|
303
301
|
return true;
|
|
304
302
|
const barbellSubstitutes = [
|
|
@@ -316,7 +314,7 @@ const isBarbellExerciseAllowed = (exercise, workoutBarbellExerciseCount, userEqu
|
|
|
316
314
|
const isExerciseUsed = (exercise, context) => {
|
|
317
315
|
var _a, _b, _c;
|
|
318
316
|
return (((_a = context.programUsedExerciseIds) === null || _a === void 0 ? void 0 : _a.has(exercise.id)) ||
|
|
319
|
-
((_b = context.
|
|
317
|
+
((_b = context.routineUsedExerciseIds) === null || _b === void 0 ? void 0 : _b.has(exercise.id)) ||
|
|
320
318
|
((_c = context.excludedExerciseIds) === null || _c === void 0 ? void 0 : _c.has(exercise.id)) ||
|
|
321
319
|
false);
|
|
322
320
|
};
|
|
@@ -395,16 +393,16 @@ const getPrioritySortedExercises = (exercisePriorities, exerciseStore) => {
|
|
|
395
393
|
return sortedExercises;
|
|
396
394
|
};
|
|
397
395
|
exports.getPrioritySortedExercises = getPrioritySortedExercises;
|
|
398
|
-
const getMuscleGroup = ({ muscleGroupPrescription, programFocusMuscleExerciseCount, focusMuscle,
|
|
396
|
+
const getMuscleGroup = ({ muscleGroupPrescription, programFocusMuscleExerciseCount, focusMuscle, routineName, }) => {
|
|
399
397
|
if (muscleGroupPrescription === 'focus_muscle') {
|
|
400
|
-
// If the user has selected a focus muscle and it is allowed for the
|
|
398
|
+
// If the user has selected a focus muscle and it is allowed for the routine,
|
|
401
399
|
// we return the focus muscle extra exercise
|
|
402
400
|
if (!!focusMuscle &&
|
|
403
|
-
isFocusMuscleExtraExerciseAllowed(
|
|
401
|
+
isFocusMuscleExtraExerciseAllowed(routineName, focusMuscle)) {
|
|
404
402
|
const n = _1.simplifiedMuscleGroupToMuscleGroups[focusMuscle].length;
|
|
405
403
|
return _1.simplifiedMuscleGroupToMuscleGroups[focusMuscle][programFocusMuscleExerciseCount % n];
|
|
406
404
|
}
|
|
407
|
-
// If the user has not selected a focus muscle or it is not allowed for the
|
|
405
|
+
// If the user has not selected a focus muscle or it is not allowed for the routine
|
|
408
406
|
// we skip this extra exercise for the focus muscle in the program
|
|
409
407
|
return undefined;
|
|
410
408
|
}
|
|
@@ -422,7 +420,7 @@ const isExerciseMatch = (exercise, criteria) => {
|
|
|
422
420
|
const levelMatch = (_b = (_a = exercise.level) === null || _a === void 0 ? void 0 : _a.includes(criteria.level)) !== null && _b !== void 0 ? _b : false;
|
|
423
421
|
const goalMatch = (_d = (_c = exercise.goal) === null || _c === void 0 ? void 0 : _c.includes(criteria.goal)) !== null && _d !== void 0 ? _d : false;
|
|
424
422
|
const equipmentMatch = (0, exports.isEquipmentCompatible)(exercise, criteria.equipments);
|
|
425
|
-
const barbellLimitOk = isBarbellExerciseAllowed(exercise, criteria.
|
|
423
|
+
const barbellLimitOk = isBarbellExerciseAllowed(exercise, criteria.routineBarbellExerciseCount, criteria.equipments, criteria.frequency);
|
|
426
424
|
return (categoryMatch && levelMatch && goalMatch && equipmentMatch && barbellLimitOk);
|
|
427
425
|
};
|
|
428
426
|
/**
|
|
@@ -467,8 +465,8 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
467
465
|
programUsedExerciseIds: context.programUsedExerciseIds,
|
|
468
466
|
excludedExerciseIds: context.excludedExerciseIds,
|
|
469
467
|
};
|
|
470
|
-
const
|
|
471
|
-
|
|
468
|
+
const routineContext = {
|
|
469
|
+
routineUsedExerciseIds: context.routineUsedExerciseIds,
|
|
472
470
|
excludedExerciseIds: context.excludedExerciseIds,
|
|
473
471
|
};
|
|
474
472
|
// Pass 1
|
|
@@ -482,10 +480,10 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
482
480
|
if (exercise)
|
|
483
481
|
return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 1 }) };
|
|
484
482
|
// Pass 2
|
|
485
|
-
exercise = findMatchingExercise(exercises, criteria,
|
|
483
|
+
exercise = findMatchingExercise(exercises, criteria, routineContext);
|
|
486
484
|
trace.entries.push({
|
|
487
485
|
pass: 2,
|
|
488
|
-
label: 'exact match; not used in
|
|
486
|
+
label: 'exact match; not used in routine (reuse allowed in program)',
|
|
489
487
|
candidatePoolSize: exercises.length,
|
|
490
488
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
491
489
|
});
|
|
@@ -502,20 +500,20 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
502
500
|
if (exercise)
|
|
503
501
|
return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 3 }) };
|
|
504
502
|
// Pass 4
|
|
505
|
-
exercise = findAlternativeIsolationExercise(exercises, criteria,
|
|
503
|
+
exercise = findAlternativeIsolationExercise(exercises, criteria, routineContext);
|
|
506
504
|
trace.entries.push({
|
|
507
505
|
pass: 4,
|
|
508
|
-
label: 'handpicked alternative isolation; not used in
|
|
506
|
+
label: 'handpicked alternative isolation; not used in routine (reuse allowed in program)',
|
|
509
507
|
candidatePoolSize: exercises.length,
|
|
510
508
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
511
509
|
});
|
|
512
510
|
if (exercise)
|
|
513
511
|
return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 4 }) };
|
|
514
512
|
// Pass 5
|
|
515
|
-
exercise = findMatchingExercise(exercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }),
|
|
513
|
+
exercise = findMatchingExercise(exercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }), routineContext);
|
|
516
514
|
trace.entries.push({
|
|
517
515
|
pass: 5,
|
|
518
|
-
label: 'match regardless of isolation or compound; not used in
|
|
516
|
+
label: 'match regardless of isolation or compound; not used in routine (reuse allowed in program)',
|
|
519
517
|
candidatePoolSize: exercises.length,
|
|
520
518
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
521
519
|
});
|
|
@@ -525,10 +523,10 @@ const pickTrainerExerciseWithTrace = (params) => {
|
|
|
525
523
|
const secondaryMuscleExercises = Object.values(sortedExercises)
|
|
526
524
|
.flat()
|
|
527
525
|
.filter((e) => e.other_muscles.includes(criteria.muscleGroup));
|
|
528
|
-
exercise = findMatchingExercise(secondaryMuscleExercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }),
|
|
526
|
+
exercise = findMatchingExercise(secondaryMuscleExercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }), routineContext);
|
|
529
527
|
trace.entries.push({
|
|
530
528
|
pass: 6,
|
|
531
|
-
label: 'secondary muscle match; regardless of isolation or compound; not used in
|
|
529
|
+
label: 'secondary muscle match; regardless of isolation or compound; not used in routine (reuse allowed in program)',
|
|
532
530
|
candidatePoolSize: secondaryMuscleExercises.length,
|
|
533
531
|
selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
|
|
534
532
|
});
|
|
@@ -544,11 +542,11 @@ function pickTrainerExercise(params) {
|
|
|
544
542
|
}
|
|
545
543
|
const CARDIO_TRACE_INDEX_BEFORE_TEMPLATE = -1;
|
|
546
544
|
exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = 600;
|
|
547
|
-
/** Inserts optional cardio before or after template exercises for one
|
|
548
|
-
const
|
|
545
|
+
/** Inserts optional cardio before or after template exercises for one routine. */
|
|
546
|
+
const attachOptionalCardioToRoutine = ({ cardioPreference, sortedExercises, criteria, debugExerciseSelectionTrace, exerciseSelectionTraces, routine, templatePrescriptionCount, routineExercises, }) => {
|
|
549
547
|
if (cardioPreference === 'no-cardio')
|
|
550
548
|
return;
|
|
551
|
-
// Cardio is reusable across
|
|
549
|
+
// Cardio is reusable across routines, so program/routine used-id contexts are
|
|
552
550
|
// intentionally omitted — only the global excluded set applies.
|
|
553
551
|
const selectionParams = {
|
|
554
552
|
sortedExercises,
|
|
@@ -559,7 +557,7 @@ const attachOptionalCardioToWorkoutTemplate = ({ cardioPreference, sortedExercis
|
|
|
559
557
|
equipments: criteria.equipments,
|
|
560
558
|
muscleGroup: 'cardio',
|
|
561
559
|
exerciseCategory: 'all',
|
|
562
|
-
|
|
560
|
+
routineBarbellExerciseCount: 0,
|
|
563
561
|
},
|
|
564
562
|
context: { excludedExerciseIds: criteria.excludedExerciseIds },
|
|
565
563
|
};
|
|
@@ -569,18 +567,17 @@ const attachOptionalCardioToWorkoutTemplate = ({ cardioPreference, sortedExercis
|
|
|
569
567
|
return;
|
|
570
568
|
const slot = {
|
|
571
569
|
kind: 'cardio',
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
set_count: 1,
|
|
570
|
+
exerciseTemplate: cardioExercise,
|
|
571
|
+
durationSeconds: exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS,
|
|
575
572
|
};
|
|
576
573
|
if (cardioPreference === 'workout-start')
|
|
577
|
-
|
|
574
|
+
routineExercises.unshift(slot);
|
|
578
575
|
else
|
|
579
|
-
|
|
576
|
+
routineExercises.push(slot);
|
|
580
577
|
if (debugExerciseSelectionTrace) {
|
|
581
578
|
exerciseSelectionTraces.push({
|
|
582
579
|
traceKind: 'cardio',
|
|
583
|
-
|
|
580
|
+
routine,
|
|
584
581
|
prescriptionIndex: cardioPreference === 'workout-start'
|
|
585
582
|
? CARDIO_TRACE_INDEX_BEFORE_TEMPLATE
|
|
586
583
|
: templatePrescriptionCount,
|
|
@@ -595,27 +592,28 @@ function generateProgram(params) {
|
|
|
595
592
|
var _a, _b;
|
|
596
593
|
const { trainerAlgorithmSettings, frequency, goal, level, equipments, workoutDurationMinutes, restTimerLength, exerciseStore, focusMuscle, excludedExerciseIds, debugExerciseSelectionTrace, cardioPreference, } = params;
|
|
597
594
|
const exerciseSelectionTraces = [];
|
|
598
|
-
const
|
|
595
|
+
const routines = exports.programSplits[frequency];
|
|
599
596
|
const program = {
|
|
600
|
-
|
|
597
|
+
name: frequency,
|
|
598
|
+
routines: [],
|
|
601
599
|
};
|
|
602
600
|
// TODO: Rename sortedExercises to sortedExercisesByMuscleGroup or something similar
|
|
603
601
|
const sortedExercises = (0, exports.getPrioritySortedExercises)(trainerAlgorithmSettings.exercise_priorities, exerciseStore);
|
|
604
602
|
const programUsedExerciseIds = new Set();
|
|
605
603
|
let programFocusMuscleExerciseCount = 0;
|
|
606
604
|
const allErrors = [];
|
|
607
|
-
for (const
|
|
608
|
-
const
|
|
609
|
-
let
|
|
610
|
-
const
|
|
611
|
-
const
|
|
612
|
-
for (let prescriptionIndex = 0; prescriptionIndex <
|
|
613
|
-
const exercisePrescription =
|
|
605
|
+
for (const routine of routines) {
|
|
606
|
+
const routineTemplate = trainerAlgorithmSettings.templates[routine];
|
|
607
|
+
let routineBarbellExerciseCount = 0;
|
|
608
|
+
const routineUsedExerciseIds = new Set();
|
|
609
|
+
const routineExercises = [];
|
|
610
|
+
for (let prescriptionIndex = 0; prescriptionIndex < routineTemplate.exercises.length; prescriptionIndex++) {
|
|
611
|
+
const exercisePrescription = routineTemplate.exercises[prescriptionIndex];
|
|
614
612
|
const muscleGroup = getMuscleGroup({
|
|
615
613
|
muscleGroupPrescription: exercisePrescription.muscle_group,
|
|
616
614
|
programFocusMuscleExerciseCount,
|
|
617
615
|
focusMuscle,
|
|
618
|
-
|
|
616
|
+
routineName: routine,
|
|
619
617
|
});
|
|
620
618
|
// If the muscle group is not found, skip the exercise
|
|
621
619
|
if (!muscleGroup) {
|
|
@@ -634,7 +632,7 @@ function generateProgram(params) {
|
|
|
634
632
|
exerciseCategory: exercisePrescription.category,
|
|
635
633
|
equipments,
|
|
636
634
|
muscleGroup,
|
|
637
|
-
|
|
635
|
+
routineBarbellExerciseCount,
|
|
638
636
|
level,
|
|
639
637
|
goal,
|
|
640
638
|
};
|
|
@@ -645,7 +643,7 @@ function generateProgram(params) {
|
|
|
645
643
|
criteria,
|
|
646
644
|
context: {
|
|
647
645
|
programUsedExerciseIds,
|
|
648
|
-
|
|
646
|
+
routineUsedExerciseIds,
|
|
649
647
|
excludedExerciseIds,
|
|
650
648
|
},
|
|
651
649
|
withTrace: true,
|
|
@@ -653,7 +651,7 @@ function generateProgram(params) {
|
|
|
653
651
|
exercise = selection.exercise;
|
|
654
652
|
exerciseSelectionTraces.push({
|
|
655
653
|
traceKind: 'template',
|
|
656
|
-
|
|
654
|
+
routine,
|
|
657
655
|
prescriptionIndex,
|
|
658
656
|
prescription: exercisePrescription,
|
|
659
657
|
resolvedMuscleGroup: muscleGroup,
|
|
@@ -668,29 +666,30 @@ function generateProgram(params) {
|
|
|
668
666
|
criteria,
|
|
669
667
|
context: {
|
|
670
668
|
programUsedExerciseIds,
|
|
671
|
-
|
|
669
|
+
routineUsedExerciseIds,
|
|
672
670
|
excludedExerciseIds,
|
|
673
671
|
},
|
|
674
672
|
});
|
|
675
673
|
}
|
|
676
674
|
if (!!exercise) {
|
|
677
675
|
programUsedExerciseIds.add(exercise.id);
|
|
678
|
-
|
|
676
|
+
routineUsedExerciseIds.add(exercise.id);
|
|
679
677
|
if (exercise.equipment_category === 'barbell')
|
|
680
|
-
|
|
678
|
+
routineBarbellExerciseCount++;
|
|
681
679
|
if (exercisePrescription.muscle_group === 'focus_muscle')
|
|
682
680
|
programFocusMuscleExerciseCount++;
|
|
683
681
|
const repRange = (0, exports.getTrainerRepRange)(trainerAlgorithmSettings, goal, exercisePrescription.category);
|
|
684
|
-
|
|
682
|
+
routineExercises.push({
|
|
685
683
|
kind: 'resistance',
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
684
|
+
exerciseTemplate: exercise,
|
|
685
|
+
muscleGroup: exercisePrescription.muscle_group,
|
|
686
|
+
category: exercisePrescription.category,
|
|
687
|
+
sets: (0, exports.getTrainerSetCount)(trainerAlgorithmSettings, goal, frequency),
|
|
688
|
+
repRangeStart: repRange.rep_range_start,
|
|
689
|
+
repRangeEnd: repRange.rep_range_end,
|
|
690
|
+
restTimerSeconds: (0, exports.getTrainerRestTimerSeconds)(trainerAlgorithmSettings, goal, restTimerLength, exercisePrescription.category),
|
|
691
|
+
warmupSetCount: exercisePrescription.warmup_set_count,
|
|
692
|
+
notes: (_b = trainerAlgorithmSettings.exercise_notes[exercise.id]) !== null && _b !== void 0 ? _b : '',
|
|
694
693
|
});
|
|
695
694
|
}
|
|
696
695
|
else {
|
|
@@ -708,21 +707,23 @@ function generateProgram(params) {
|
|
|
708
707
|
});
|
|
709
708
|
}
|
|
710
709
|
}
|
|
711
|
-
|
|
710
|
+
attachOptionalCardioToRoutine({
|
|
712
711
|
cardioPreference,
|
|
713
712
|
sortedExercises,
|
|
714
713
|
criteria: { equipments, level, goal, frequency, excludedExerciseIds },
|
|
715
714
|
debugExerciseSelectionTrace,
|
|
716
715
|
exerciseSelectionTraces,
|
|
717
|
-
|
|
718
|
-
templatePrescriptionCount:
|
|
719
|
-
|
|
716
|
+
routine,
|
|
717
|
+
templatePrescriptionCount: routineTemplate.exercises.length,
|
|
718
|
+
routineExercises,
|
|
720
719
|
});
|
|
721
|
-
program.
|
|
722
|
-
name:
|
|
723
|
-
|
|
720
|
+
program.routines.push({
|
|
721
|
+
name: routine,
|
|
722
|
+
notes: routineTemplate.notes,
|
|
723
|
+
exercises: routineExercises,
|
|
724
724
|
});
|
|
725
725
|
}
|
|
726
|
+
// Return result based on whether there were errors
|
|
726
727
|
if (allErrors.length > 0) {
|
|
727
728
|
const result = {
|
|
728
729
|
success: false,
|