hevy-shared 1.0.974 → 1.0.975

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.
@@ -59,7 +59,7 @@ export interface ProgramGenerationParams<T extends HevyTrainerLibraryExercise> {
59
59
  exerciseStore: T[];
60
60
  focusMuscle?: SimplifiedMuscleGroup;
61
61
  excludedExerciseIds?: Set<string>;
62
- cardio: CardioPreference;
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 ProgramExerciseSelectionTraceRecord {
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
- export interface TrainerProgramExercise {
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;
@@ -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, cardio, } = params;
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
- if (cardio) {
657
- const cardioExercises = sortedExercises['cardio'];
658
- const cardioExercise = findMatchingExercise(cardioExercises, {
659
- frequency,
660
- exerciseCategory: 'all',
661
- equipments,
662
- muscleGroup: 'cardio',
663
- routineBarbellExerciseCount,
664
- level,
665
- goal: 'all',
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
@@ -1295,7 +1295,6 @@ export interface UpdateHevyTrainerProgramRequestBody {
1295
1295
  next_workout_index?: number;
1296
1296
  workout_duration_minutes?: WorkoutDurationMinutes;
1297
1297
  rest_timer_length?: RestTimerLength;
1298
- cardio_preference?: CardioPreference;
1299
1298
  routines: {
1300
1299
  id: string;
1301
1300
  title: string;
@@ -0,0 +1 @@
1
+ export {};