hevy-shared 1.0.974 → 1.0.976
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 +31 -3
- package/built/hevyTrainer.js +80 -86
- package/built/index.d.ts +1 -1
- package/built/tests/hevyTrainer.test.d.ts +1 -0
- package/built/tests/hevyTrainer.test.js +1423 -0
- package/package.json +1 -1
package/built/hevyTrainer.d.ts
CHANGED
|
@@ -59,7 +59,7 @@ export interface ProgramGenerationParams<T extends HevyTrainerLibraryExercise> {
|
|
|
59
59
|
exerciseStore: T[];
|
|
60
60
|
focusMuscle?: SimplifiedMuscleGroup;
|
|
61
61
|
excludedExerciseIds?: Set<string>;
|
|
62
|
-
|
|
62
|
+
cardioPreference: CardioPreference;
|
|
63
63
|
/**
|
|
64
64
|
* When enabled, includes exercise selection trace breadcrumbs in the result
|
|
65
65
|
* for debugging. Intended for troubleshooting on backoffice; keep off in
|
|
@@ -67,8 +67,10 @@ export interface ProgramGenerationParams<T extends HevyTrainerLibraryExercise> {
|
|
|
67
67
|
*/
|
|
68
68
|
debugExerciseSelectionTrace?: boolean;
|
|
69
69
|
}
|
|
70
|
-
export interface
|
|
70
|
+
export interface TemplateExerciseSelectionTraceRecord {
|
|
71
|
+
traceKind: 'template';
|
|
71
72
|
routine: HevyTrainerRoutineName;
|
|
73
|
+
/** Template slot index. */
|
|
72
74
|
prescriptionIndex: number;
|
|
73
75
|
prescription: ExercisePrescription;
|
|
74
76
|
resolvedMuscleGroup: MuscleGroup;
|
|
@@ -76,6 +78,20 @@ export interface ProgramExerciseSelectionTraceRecord {
|
|
|
76
78
|
selectedExerciseId?: string;
|
|
77
79
|
trace: ExerciseSelectionTrace;
|
|
78
80
|
}
|
|
81
|
+
export interface CardioExerciseSelectionTraceRecord {
|
|
82
|
+
traceKind: 'cardio';
|
|
83
|
+
routine: HevyTrainerRoutineName;
|
|
84
|
+
/**
|
|
85
|
+
* Ordering key alongside template traces: `-1` when cardio is inserted before
|
|
86
|
+
* template exercises; `templatePrescriptionCount` when appended after.
|
|
87
|
+
*/
|
|
88
|
+
prescriptionIndex: number;
|
|
89
|
+
selectedExerciseId?: string;
|
|
90
|
+
equipments: GranularEquipment[];
|
|
91
|
+
level: TrainingLevel;
|
|
92
|
+
trace: ExerciseSelectionTrace;
|
|
93
|
+
}
|
|
94
|
+
export type ProgramExerciseSelectionTraceRecord = TemplateExerciseSelectionTraceRecord | CardioExerciseSelectionTraceRecord;
|
|
79
95
|
export type TrainerProgramAttemptWithTraces = TrainerProgramAttempt & {
|
|
80
96
|
exerciseSelectionTraces: ProgramExerciseSelectionTraceRecord[];
|
|
81
97
|
};
|
|
@@ -146,7 +162,9 @@ export interface TrainerAlgorithmSettings {
|
|
|
146
162
|
exercise_notes: ExerciseNotes;
|
|
147
163
|
exercise_replacements: ExerciseReplacements;
|
|
148
164
|
}
|
|
149
|
-
|
|
165
|
+
/** Resistance prescriptions from templates (sets, reps, rest, notes). */
|
|
166
|
+
export interface TrainerProgramResistanceExercise {
|
|
167
|
+
kind: 'resistance';
|
|
150
168
|
exerciseTemplate: HevyTrainerLibraryExercise;
|
|
151
169
|
muscleGroup: MuscleGroup | 'focus_muscle';
|
|
152
170
|
category: HevyTrainerExerciseCategory;
|
|
@@ -157,6 +175,15 @@ export interface TrainerProgramExercise {
|
|
|
157
175
|
restTimerSeconds: number;
|
|
158
176
|
notes?: string;
|
|
159
177
|
}
|
|
178
|
+
/** Optional cardio attachment — no prescription semantics beyond exercise choice. */
|
|
179
|
+
export interface TrainerProgramCardioExercise {
|
|
180
|
+
kind: 'cardio';
|
|
181
|
+
exerciseTemplate: HevyTrainerLibraryExercise;
|
|
182
|
+
durationSeconds: number;
|
|
183
|
+
}
|
|
184
|
+
export type TrainerProgramExercise = TrainerProgramResistanceExercise | TrainerProgramCardioExercise;
|
|
185
|
+
export declare function isTrainerProgramResistanceExercise(exercise: TrainerProgramExercise): exercise is TrainerProgramResistanceExercise;
|
|
186
|
+
export declare function isTrainerProgramCardioExercise(exercise: TrainerProgramExercise): exercise is TrainerProgramCardioExercise;
|
|
160
187
|
export interface TrainerProgramRoutine {
|
|
161
188
|
name: HevyTrainerRoutineName;
|
|
162
189
|
exercises: TrainerProgramExercise[];
|
|
@@ -209,6 +236,7 @@ export declare function pickExerciseForPrescription<T extends HevyTrainerLibrary
|
|
|
209
236
|
}): PickExerciseResult<T>;
|
|
210
237
|
export declare function pickExerciseForPrescription<T extends HevyTrainerLibraryExercise>(params: ExerciseSelectionParams<T>): T | undefined;
|
|
211
238
|
export type HevyTrainerLibraryExercise = Pick<LibraryExercise, 'id' | 'title' | 'priority' | 'muscle_group' | 'other_muscles' | 'exercise_type' | 'equipment_category' | 'category' | 'level' | 'goal' | 'granular_equipments'>;
|
|
239
|
+
export declare const DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = 600;
|
|
212
240
|
export interface ExercisePrescriptionError {
|
|
213
241
|
type: 'exercise_not_found';
|
|
214
242
|
prescription: ExercisePrescription;
|
package/built/hevyTrainer.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPrioritySortedExercises = exports.isEquipmentCompatible = exports.normalizeExerciseCategory = exports.getTrainerRestTimerSeconds = exports.getTrainerRepRange = exports.getTrainerSetCount = exports.programSplits = exports.frequencyMap = exports.routineNames = exports.defaultDurationPerFrequency = exports.hevyTrainerExerciseCategories = exports.granularEquipmentsToTrainerEquipments = exports.trainerEquipmentToGranularEquipments = exports.granularEquipmentDefaults = exports.trainerGymTypes = exports.workoutDurationOptions = void 0;
|
|
3
|
+
exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = exports.getPrioritySortedExercises = exports.isEquipmentCompatible = exports.normalizeExerciseCategory = exports.getTrainerRestTimerSeconds = exports.getTrainerRepRange = exports.getTrainerSetCount = exports.programSplits = exports.frequencyMap = exports.routineNames = exports.defaultDurationPerFrequency = exports.hevyTrainerExerciseCategories = exports.granularEquipmentsToTrainerEquipments = exports.trainerEquipmentToGranularEquipments = exports.granularEquipmentDefaults = exports.trainerGymTypes = exports.workoutDurationOptions = void 0;
|
|
4
|
+
exports.isTrainerProgramResistanceExercise = isTrainerProgramResistanceExercise;
|
|
5
|
+
exports.isTrainerProgramCardioExercise = isTrainerProgramCardioExercise;
|
|
4
6
|
exports.pickExerciseForPrescription = pickExerciseForPrescription;
|
|
5
7
|
exports.generateProgram = generateProgram;
|
|
6
8
|
const _1 = require(".");
|
|
@@ -197,6 +199,12 @@ exports.programSplits = {
|
|
|
197
199
|
5: ['push_1', 'pull_1', 'legs_1', 'upper_2', 'lower_2'],
|
|
198
200
|
6: ['push_2_a', 'pull_2_a', 'legs_2_a', 'push_2_b', 'pull_2_b', 'legs_2_b'],
|
|
199
201
|
};
|
|
202
|
+
function isTrainerProgramResistanceExercise(exercise) {
|
|
203
|
+
return exercise.kind === 'resistance';
|
|
204
|
+
}
|
|
205
|
+
function isTrainerProgramCardioExercise(exercise) {
|
|
206
|
+
return exercise.kind === 'cardio';
|
|
207
|
+
}
|
|
200
208
|
const getTrainerSetCount = (trainerAlgorithmSettings, goal, frequency) => {
|
|
201
209
|
return trainerAlgorithmSettings.sets[exports.frequencyMap[frequency]][goal];
|
|
202
210
|
};
|
|
@@ -536,9 +544,67 @@ function pickExerciseForPrescription(params) {
|
|
|
536
544
|
}
|
|
537
545
|
return pickExerciseForPrescriptionWithTrace(params).exercise;
|
|
538
546
|
}
|
|
547
|
+
const CARDIO_TRACE_INDEX_BEFORE_TEMPLATE = -1;
|
|
548
|
+
exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = 600;
|
|
549
|
+
function appendCardioExerciseSelectionTrace(traces, params) {
|
|
550
|
+
traces.push({
|
|
551
|
+
traceKind: 'cardio',
|
|
552
|
+
routine: params.routine,
|
|
553
|
+
prescriptionIndex: params.prescriptionIndex,
|
|
554
|
+
selectedExerciseId: params.selectedExerciseId,
|
|
555
|
+
equipments: params.equipments,
|
|
556
|
+
level: params.level,
|
|
557
|
+
trace: {
|
|
558
|
+
entries: [
|
|
559
|
+
{
|
|
560
|
+
pass: 1,
|
|
561
|
+
label: 'cardio: first priority exercise matching equipment + level; globally excluded IDs skipped',
|
|
562
|
+
candidatePoolSize: params.candidatePoolSize,
|
|
563
|
+
selectedExerciseId: params.selectedExerciseId,
|
|
564
|
+
},
|
|
565
|
+
],
|
|
566
|
+
selectedPass: 1,
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
/** Inserts optional cardio before or after template exercises for one routine. */
|
|
571
|
+
const attachOptionalCardioToRoutine = ({ cardioPreference, cardioExercises, criteria, debugExerciseSelectionTrace, exerciseSelectionTraces, routine, templatePrescriptionCount, routineExercises, }) => {
|
|
572
|
+
if (!cardioPreference)
|
|
573
|
+
return;
|
|
574
|
+
const cardioExercise = cardioExercises.find((ex) => {
|
|
575
|
+
var _a, _b, _c;
|
|
576
|
+
const equipmentCompatible = (0, exports.isEquipmentCompatible)(ex, criteria.equipments);
|
|
577
|
+
const levelCompatible = (_a = ex.level) === null || _a === void 0 ? void 0 : _a.includes(criteria.level);
|
|
578
|
+
const excluded = (_c = (_b = criteria.excludedExerciseIds) === null || _b === void 0 ? void 0 : _b.has(ex.id)) !== null && _c !== void 0 ? _c : false;
|
|
579
|
+
return equipmentCompatible && levelCompatible && !excluded;
|
|
580
|
+
});
|
|
581
|
+
if (!cardioExercise)
|
|
582
|
+
return;
|
|
583
|
+
const slot = {
|
|
584
|
+
kind: 'cardio',
|
|
585
|
+
exerciseTemplate: cardioExercise,
|
|
586
|
+
durationSeconds: exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS,
|
|
587
|
+
};
|
|
588
|
+
if (cardioPreference === 'beginning')
|
|
589
|
+
routineExercises.unshift(slot);
|
|
590
|
+
else
|
|
591
|
+
routineExercises.push(slot);
|
|
592
|
+
if (debugExerciseSelectionTrace) {
|
|
593
|
+
appendCardioExerciseSelectionTrace(exerciseSelectionTraces, {
|
|
594
|
+
routine,
|
|
595
|
+
prescriptionIndex: cardioPreference === 'beginning'
|
|
596
|
+
? CARDIO_TRACE_INDEX_BEFORE_TEMPLATE
|
|
597
|
+
: templatePrescriptionCount,
|
|
598
|
+
selectedExerciseId: cardioExercise.id,
|
|
599
|
+
equipments: criteria.equipments,
|
|
600
|
+
level: criteria.level,
|
|
601
|
+
candidatePoolSize: cardioExercises.length,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
};
|
|
539
605
|
function generateProgram(params) {
|
|
540
606
|
var _a, _b;
|
|
541
|
-
const { trainerAlgorithmSettings, frequency, goal, level, equipments, workoutDurationMinutes, restTimerLength, exerciseStore, focusMuscle, excludedExerciseIds, debugExerciseSelectionTrace,
|
|
607
|
+
const { trainerAlgorithmSettings, frequency, goal, level, equipments, workoutDurationMinutes, restTimerLength, exerciseStore, focusMuscle, excludedExerciseIds, debugExerciseSelectionTrace, cardioPreference, } = params;
|
|
542
608
|
const exerciseSelectionTraces = [];
|
|
543
609
|
const routines = exports.programSplits[frequency];
|
|
544
610
|
const program = {
|
|
@@ -598,6 +664,7 @@ function generateProgram(params) {
|
|
|
598
664
|
});
|
|
599
665
|
exercise = selection.exercise;
|
|
600
666
|
exerciseSelectionTraces.push({
|
|
667
|
+
traceKind: 'template',
|
|
601
668
|
routine,
|
|
602
669
|
prescriptionIndex,
|
|
603
670
|
prescription: exercisePrescription,
|
|
@@ -627,6 +694,7 @@ function generateProgram(params) {
|
|
|
627
694
|
programFocusMuscleExerciseCount++;
|
|
628
695
|
const repRange = (0, exports.getTrainerRepRange)(trainerAlgorithmSettings, goal, exercisePrescription.category);
|
|
629
696
|
routineExercises.push({
|
|
697
|
+
kind: 'resistance',
|
|
630
698
|
exerciseTemplate: exercise,
|
|
631
699
|
muscleGroup: exercisePrescription.muscle_group,
|
|
632
700
|
category: exercisePrescription.category,
|
|
@@ -653,90 +721,16 @@ function generateProgram(params) {
|
|
|
653
721
|
});
|
|
654
722
|
}
|
|
655
723
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
}, {
|
|
667
|
-
programUsedExerciseIds,
|
|
668
|
-
routineUsedExerciseIds,
|
|
669
|
-
excludedExerciseIds,
|
|
670
|
-
});
|
|
671
|
-
// TODO change the interface to include cardio
|
|
672
|
-
if (cardioExercise) {
|
|
673
|
-
if (cardio === 'beginning') {
|
|
674
|
-
routineExercises.unshift({
|
|
675
|
-
exerciseTemplate: cardioExercise,
|
|
676
|
-
muscleGroup: 'cardio',
|
|
677
|
-
category: 'compound',
|
|
678
|
-
sets: 1,
|
|
679
|
-
repRangeStart: 0,
|
|
680
|
-
repRangeEnd: 0,
|
|
681
|
-
restTimerSeconds: 0,
|
|
682
|
-
});
|
|
683
|
-
exerciseSelectionTraces.unshift({
|
|
684
|
-
routine,
|
|
685
|
-
prescriptionIndex: 0,
|
|
686
|
-
prescription: { muscle_group: 'cardio', category: 'compound' },
|
|
687
|
-
resolvedMuscleGroup: 'cardio',
|
|
688
|
-
criteria: {
|
|
689
|
-
frequency,
|
|
690
|
-
exerciseCategory: 'all',
|
|
691
|
-
equipments,
|
|
692
|
-
muscleGroup: 'cardio',
|
|
693
|
-
routineBarbellExerciseCount,
|
|
694
|
-
level,
|
|
695
|
-
goal: 'all',
|
|
696
|
-
},
|
|
697
|
-
selectedExerciseId: cardioExercise.id,
|
|
698
|
-
trace: {
|
|
699
|
-
entries: [],
|
|
700
|
-
selectedPass: 1,
|
|
701
|
-
},
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
else {
|
|
705
|
-
routineExercises.push({
|
|
706
|
-
exerciseTemplate: cardioExercise,
|
|
707
|
-
muscleGroup: 'cardio',
|
|
708
|
-
category: 'compound',
|
|
709
|
-
sets: 1,
|
|
710
|
-
repRangeStart: 0,
|
|
711
|
-
repRangeEnd: 0,
|
|
712
|
-
restTimerSeconds: 0,
|
|
713
|
-
});
|
|
714
|
-
// todo: handle debug traces with the exercises
|
|
715
|
-
exerciseSelectionTraces.push({
|
|
716
|
-
routine,
|
|
717
|
-
prescriptionIndex: routineExercises.length,
|
|
718
|
-
prescription: { muscle_group: 'cardio', category: 'compound' },
|
|
719
|
-
resolvedMuscleGroup: 'cardio',
|
|
720
|
-
criteria: {
|
|
721
|
-
frequency,
|
|
722
|
-
exerciseCategory: 'all',
|
|
723
|
-
equipments,
|
|
724
|
-
muscleGroup: 'cardio',
|
|
725
|
-
routineBarbellExerciseCount,
|
|
726
|
-
level,
|
|
727
|
-
goal: 'all',
|
|
728
|
-
},
|
|
729
|
-
selectedExerciseId: cardioExercise.id,
|
|
730
|
-
trace: {
|
|
731
|
-
entries: [],
|
|
732
|
-
selectedPass: 1,
|
|
733
|
-
},
|
|
734
|
-
});
|
|
735
|
-
}
|
|
736
|
-
programUsedExerciseIds.add(cardioExercise.id);
|
|
737
|
-
routineUsedExerciseIds.add(cardioExercise.id);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
724
|
+
attachOptionalCardioToRoutine({
|
|
725
|
+
cardioPreference,
|
|
726
|
+
cardioExercises: sortedExercises.cardio,
|
|
727
|
+
criteria: { equipments, level, excludedExerciseIds },
|
|
728
|
+
debugExerciseSelectionTrace,
|
|
729
|
+
exerciseSelectionTraces,
|
|
730
|
+
routine,
|
|
731
|
+
templatePrescriptionCount: routineTemplate.exercises.length,
|
|
732
|
+
routineExercises,
|
|
733
|
+
});
|
|
740
734
|
program.routines.push({
|
|
741
735
|
name: routine,
|
|
742
736
|
notes: routineTemplate.notes,
|
package/built/index.d.ts
CHANGED
|
@@ -1293,9 +1293,9 @@ export interface UpdateHevyTrainerProgramRequestBody {
|
|
|
1293
1293
|
weekly_frequency: WeeklyTrainingFrequency;
|
|
1294
1294
|
focus_muscle?: SimplifiedMuscleGroup;
|
|
1295
1295
|
next_workout_index?: number;
|
|
1296
|
+
cardio_preference?: CardioPreference;
|
|
1296
1297
|
workout_duration_minutes?: WorkoutDurationMinutes;
|
|
1297
1298
|
rest_timer_length?: RestTimerLength;
|
|
1298
|
-
cardio_preference?: CardioPreference;
|
|
1299
1299
|
routines: {
|
|
1300
1300
|
id: string;
|
|
1301
1301
|
title: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|