hevy-shared 1.0.1047 → 1.0.1049

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.
@@ -0,0 +1 @@
1
+ export declare const distanceMBetween: (lat1: number, lon1: number, lat2: number, lon2: number) => number;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.distanceMBetween = void 0;
4
+ const haversineDistance = (lat1, lon1, lat2, lon2) => {
5
+ const R = 6371;
6
+ const dLat = ((lat2 - lat1) * Math.PI) / 180;
7
+ const dLon = ((lon2 - lon1) * Math.PI) / 180;
8
+ const a = Math.sin(dLat / 2) ** 2 +
9
+ Math.cos((lat1 * Math.PI) / 180) *
10
+ Math.cos((lat2 * Math.PI) / 180) *
11
+ Math.sin(dLon / 2) ** 2;
12
+ return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
13
+ };
14
+ const distanceMBetween = (lat1, lon1, lat2, lon2) => Math.round(haversineDistance(lat1, lon1, lat2, lon2) * 1000);
15
+ exports.distanceMBetween = distanceMBetween;
@@ -1,6 +1,6 @@
1
- import { WeeklyTrainingFrequency, TrainingGoal, TrainingLevel, SimplifiedMuscleGroup, MuscleGroup, LibraryExercise, ExerciseCategory, GranularEquipment, HevyTrainerProgramEquipment, RestTimerLength, CardioPreference } from '.';
1
+ import { WeeklyTrainingFrequency, TrainingGoal, TrainingLevel, SimplifiedMuscleGroup, MuscleGroup, LibraryExercise, ExerciseCategory, GranularEquipment, HevyTrainerProgramEquipment, RestTimerLength, CardioPreference, StrictRepRange } from '.';
2
2
  export type HevyTrainerExerciseCategory = (typeof hevyTrainerExerciseCategories)[number];
3
- export type HevyTrainerRoutineName = (typeof routineNames)[number];
3
+ export type TrainerWorkoutTemplateName = (typeof workoutTemplateNames)[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 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"];
19
+ export declare const workoutTemplateNames: 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
- routineBarbellExerciseCount: number;
24
+ workoutBarbellExerciseCount: 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
- routineUsedExerciseIds?: Set<string>;
32
+ workoutUsedExerciseIds?: 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
- routine: HevyTrainerRoutineName;
72
+ workoutTemplate: TrainerWorkoutTemplateName;
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
- routine: HevyTrainerRoutineName;
82
+ workoutTemplate: TrainerWorkoutTemplateName;
83
83
  prescriptionIndex: number;
84
84
  selectedExerciseId?: string;
85
85
  equipments: GranularEquipment[];
@@ -92,19 +92,19 @@ export type TrainerProgramAttemptWithTraces = TrainerProgramAttempt & {
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, HevyTrainerRoutineName[]>;
95
+ export declare const programSplits: Record<WeeklyTrainingFrequency, TrainerWorkoutTemplateName[]>;
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 RepRange {
102
+ export interface AlgorithmRepRange {
103
103
  rep_range_start: number;
104
104
  rep_range_end: number;
105
105
  }
106
106
  export type RepRangesPerCategory = {
107
- [key in HevyTrainerExerciseCategory]: RepRange;
107
+ [key in HevyTrainerExerciseCategory]: AlgorithmRepRange;
108
108
  };
109
109
  export type RepRanges = {
110
110
  [key in TrainingGoal]: RepRangesPerCategory;
@@ -135,10 +135,11 @@ 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 */
138
139
  notes?: string;
139
140
  }
140
141
  export type Templates = {
141
- [key in HevyTrainerRoutineName]: WorkoutTemplate;
142
+ [key in TrainerWorkoutTemplateName]: WorkoutTemplate;
142
143
  };
143
144
  export interface BackofficeTrainerPreset {
144
145
  id?: number;
@@ -157,39 +158,38 @@ export interface TrainerAlgorithmSettings {
157
158
  exercise_notes: ExerciseNotes;
158
159
  exercise_replacements: ExerciseReplacements;
159
160
  }
160
- /** Resistance prescriptions from templates (sets, reps, rest, notes). */
161
161
  export interface TrainerProgramResistanceExercise {
162
162
  kind: 'resistance';
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;
163
+ exercise_template: HevyTrainerLibraryExercise;
164
+ set_count: number;
165
+ warmup_set_count: number;
166
+ rep_range: StrictRepRange;
167
+ rest_seconds: number;
172
168
  }
173
- /** Optional cardio attachment — no prescription semantics beyond exercise choice. */
174
169
  export interface TrainerProgramCardioExercise {
175
170
  kind: 'cardio';
176
- exerciseTemplate: HevyTrainerLibraryExercise;
177
- durationSeconds: number;
171
+ exercise_template: HevyTrainerLibraryExercise;
172
+ set_count: 1;
173
+ duration_seconds: number;
178
174
  }
179
- export type TrainerProgramExercise = TrainerProgramResistanceExercise | TrainerProgramCardioExercise;
175
+ export interface TrainerProgramOtherExercise {
176
+ kind: 'other';
177
+ exercise_template: HevyTrainerLibraryExercise;
178
+ set_count: number;
179
+ }
180
+ export type TrainerProgramExercise = TrainerProgramResistanceExercise | TrainerProgramCardioExercise | TrainerProgramOtherExercise;
180
181
  export declare const isTrainerProgramResistanceExercise: (exercise: TrainerProgramExercise) => exercise is TrainerProgramResistanceExercise;
181
182
  export declare const isTrainerProgramCardioExercise: (exercise: TrainerProgramExercise) => exercise is TrainerProgramCardioExercise;
182
- export interface TrainerProgramRoutine {
183
- name: HevyTrainerRoutineName;
183
+ export declare const isTrainerProgramOtherExercise: (exercise: TrainerProgramExercise) => exercise is TrainerProgramOtherExercise;
184
+ export interface TrainerProgramWorkoutTemplate {
185
+ name: TrainerWorkoutTemplateName;
184
186
  exercises: TrainerProgramExercise[];
185
- notes?: string;
186
187
  }
187
- export interface TrainerProgram {
188
- name: WeeklyTrainingFrequency;
189
- routines: TrainerProgramRoutine[];
188
+ export interface GeneratedTrainerProgram {
189
+ workoutTemplates: TrainerProgramWorkoutTemplate[];
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) => RepRange;
192
+ export declare const getTrainerRepRange: (trainerAlgorithmSettings: TrainerAlgorithmSettings, goal: TrainingGoal, exerciseCategory: HevyTrainerExerciseCategory) => AlgorithmRepRange;
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,23 +243,14 @@ export interface ExercisePrescriptionError {
243
243
  focusMuscle?: SimplifiedMuscleGroup;
244
244
  };
245
245
  }
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
246
  export interface TrainerProgramResult {
256
247
  success: true;
257
- program: TrainerProgram;
248
+ program: GeneratedTrainerProgram;
258
249
  }
259
250
  export interface TrainerProgramError {
260
251
  success: false;
261
252
  errors: ExercisePrescriptionError[];
262
- partialProgram: TrainerProgram;
253
+ partialProgram: GeneratedTrainerProgram;
263
254
  }
264
255
  export type TrainerProgramAttempt = TrainerProgramResult | TrainerProgramError;
265
256
  /**
@@ -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.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;
3
+ exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = exports.getPrioritySortedExercises = exports.isEquipmentCompatible = exports.normalizeExerciseCategory = exports.getTrainerRestTimerSeconds = exports.getTrainerRepRange = exports.getTrainerSetCount = exports.isTrainerProgramOtherExercise = exports.isTrainerProgramCardioExercise = exports.isTrainerProgramResistanceExercise = exports.programSplits = exports.frequencyMap = exports.workoutTemplateNames = 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.routineNames = [
155
+ exports.workoutTemplateNames = [
156
156
  // Full body 1x
157
157
  'full_body_1',
158
158
  // Full body 2x
@@ -201,6 +201,8 @@ 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;
204
206
  const getTrainerSetCount = (trainerAlgorithmSettings, goal, frequency) => {
205
207
  return trainerAlgorithmSettings.sets[exports.frequencyMap[frequency]][goal];
206
208
  };
@@ -291,12 +293,12 @@ const MAX_BARBELL_EXERCISES_FOR_ONCE_PER_WEEK = 3;
291
293
  * - Only enforce the cap when the user has at least one "barbell substitute" equipment available
292
294
  * (otherwise we allow barbell exercises to avoid running out of viable options)
293
295
  */
294
- const isBarbellExerciseAllowed = (exercise, routineBarbellExerciseCount, userEquipments, frequency) => {
296
+ const isBarbellExerciseAllowed = (exercise, workoutBarbellExerciseCount, userEquipments, frequency) => {
295
297
  const isCandidateBarbell = exercise.equipment_category === 'barbell';
296
298
  const isOncePerWeek = frequency === 1;
297
299
  if (!isCandidateBarbell || !isOncePerWeek)
298
300
  return true;
299
- const isAtOrOverLimit = routineBarbellExerciseCount >= MAX_BARBELL_EXERCISES_FOR_ONCE_PER_WEEK;
301
+ const isAtOrOverLimit = workoutBarbellExerciseCount >= MAX_BARBELL_EXERCISES_FOR_ONCE_PER_WEEK;
300
302
  if (!isAtOrOverLimit)
301
303
  return true;
302
304
  const barbellSubstitutes = [
@@ -314,7 +316,7 @@ const isBarbellExerciseAllowed = (exercise, routineBarbellExerciseCount, userEqu
314
316
  const isExerciseUsed = (exercise, context) => {
315
317
  var _a, _b, _c;
316
318
  return (((_a = context.programUsedExerciseIds) === null || _a === void 0 ? void 0 : _a.has(exercise.id)) ||
317
- ((_b = context.routineUsedExerciseIds) === null || _b === void 0 ? void 0 : _b.has(exercise.id)) ||
319
+ ((_b = context.workoutUsedExerciseIds) === null || _b === void 0 ? void 0 : _b.has(exercise.id)) ||
318
320
  ((_c = context.excludedExerciseIds) === null || _c === void 0 ? void 0 : _c.has(exercise.id)) ||
319
321
  false);
320
322
  };
@@ -393,16 +395,16 @@ const getPrioritySortedExercises = (exercisePriorities, exerciseStore) => {
393
395
  return sortedExercises;
394
396
  };
395
397
  exports.getPrioritySortedExercises = getPrioritySortedExercises;
396
- const getMuscleGroup = ({ muscleGroupPrescription, programFocusMuscleExerciseCount, focusMuscle, routineName, }) => {
398
+ const getMuscleGroup = ({ muscleGroupPrescription, programFocusMuscleExerciseCount, focusMuscle, workoutTemplateName, }) => {
397
399
  if (muscleGroupPrescription === 'focus_muscle') {
398
- // If the user has selected a focus muscle and it is allowed for the routine,
400
+ // If the user has selected a focus muscle and it is allowed for the workout template,
399
401
  // we return the focus muscle extra exercise
400
402
  if (!!focusMuscle &&
401
- isFocusMuscleExtraExerciseAllowed(routineName, focusMuscle)) {
403
+ isFocusMuscleExtraExerciseAllowed(workoutTemplateName, focusMuscle)) {
402
404
  const n = _1.simplifiedMuscleGroupToMuscleGroups[focusMuscle].length;
403
405
  return _1.simplifiedMuscleGroupToMuscleGroups[focusMuscle][programFocusMuscleExerciseCount % n];
404
406
  }
405
- // If the user has not selected a focus muscle or it is not allowed for the routine
407
+ // If the user has not selected a focus muscle or it is not allowed for the workout template
406
408
  // we skip this extra exercise for the focus muscle in the program
407
409
  return undefined;
408
410
  }
@@ -420,7 +422,7 @@ const isExerciseMatch = (exercise, criteria) => {
420
422
  const levelMatch = (_b = (_a = exercise.level) === null || _a === void 0 ? void 0 : _a.includes(criteria.level)) !== null && _b !== void 0 ? _b : false;
421
423
  const goalMatch = (_d = (_c = exercise.goal) === null || _c === void 0 ? void 0 : _c.includes(criteria.goal)) !== null && _d !== void 0 ? _d : false;
422
424
  const equipmentMatch = (0, exports.isEquipmentCompatible)(exercise, criteria.equipments);
423
- const barbellLimitOk = isBarbellExerciseAllowed(exercise, criteria.routineBarbellExerciseCount, criteria.equipments, criteria.frequency);
425
+ const barbellLimitOk = isBarbellExerciseAllowed(exercise, criteria.workoutBarbellExerciseCount, criteria.equipments, criteria.frequency);
424
426
  return (categoryMatch && levelMatch && goalMatch && equipmentMatch && barbellLimitOk);
425
427
  };
426
428
  /**
@@ -465,8 +467,8 @@ const pickTrainerExerciseWithTrace = (params) => {
465
467
  programUsedExerciseIds: context.programUsedExerciseIds,
466
468
  excludedExerciseIds: context.excludedExerciseIds,
467
469
  };
468
- const routineContext = {
469
- routineUsedExerciseIds: context.routineUsedExerciseIds,
470
+ const workoutContext = {
471
+ workoutUsedExerciseIds: context.workoutUsedExerciseIds,
470
472
  excludedExerciseIds: context.excludedExerciseIds,
471
473
  };
472
474
  // Pass 1
@@ -480,10 +482,10 @@ const pickTrainerExerciseWithTrace = (params) => {
480
482
  if (exercise)
481
483
  return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 1 }) };
482
484
  // Pass 2
483
- exercise = findMatchingExercise(exercises, criteria, routineContext);
485
+ exercise = findMatchingExercise(exercises, criteria, workoutContext);
484
486
  trace.entries.push({
485
487
  pass: 2,
486
- label: 'exact match; not used in routine (reuse allowed in program)',
488
+ label: 'exact match; not used in the same workout (reuse allowed in program)',
487
489
  candidatePoolSize: exercises.length,
488
490
  selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
489
491
  });
@@ -500,20 +502,20 @@ const pickTrainerExerciseWithTrace = (params) => {
500
502
  if (exercise)
501
503
  return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 3 }) };
502
504
  // Pass 4
503
- exercise = findAlternativeIsolationExercise(exercises, criteria, routineContext);
505
+ exercise = findAlternativeIsolationExercise(exercises, criteria, workoutContext);
504
506
  trace.entries.push({
505
507
  pass: 4,
506
- label: 'handpicked alternative isolation; not used in routine (reuse allowed in program)',
508
+ label: 'handpicked alternative isolation; not used in the same workout (reuse allowed in program)',
507
509
  candidatePoolSize: exercises.length,
508
510
  selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
509
511
  });
510
512
  if (exercise)
511
513
  return { exercise, trace: Object.assign(Object.assign({}, trace), { selectedPass: 4 }) };
512
514
  // Pass 5
513
- exercise = findMatchingExercise(exercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }), routineContext);
515
+ exercise = findMatchingExercise(exercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }), workoutContext);
514
516
  trace.entries.push({
515
517
  pass: 5,
516
- label: 'match regardless of isolation or compound; not used in routine (reuse allowed in program)',
518
+ label: 'match regardless of isolation or compound; not used in the same workout (reuse allowed in program)',
517
519
  candidatePoolSize: exercises.length,
518
520
  selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
519
521
  });
@@ -523,10 +525,10 @@ const pickTrainerExerciseWithTrace = (params) => {
523
525
  const secondaryMuscleExercises = Object.values(sortedExercises)
524
526
  .flat()
525
527
  .filter((e) => e.other_muscles.includes(criteria.muscleGroup));
526
- exercise = findMatchingExercise(secondaryMuscleExercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }), routineContext);
528
+ exercise = findMatchingExercise(secondaryMuscleExercises, Object.assign(Object.assign({}, criteria), { exerciseCategory: 'all' }), workoutContext);
527
529
  trace.entries.push({
528
530
  pass: 6,
529
- label: 'secondary muscle match; regardless of isolation or compound; not used in routine (reuse allowed in program)',
531
+ label: 'secondary muscle match; regardless of isolation or compound; not used in the same workout (reuse allowed in program)',
530
532
  candidatePoolSize: secondaryMuscleExercises.length,
531
533
  selectedExerciseId: exercise === null || exercise === void 0 ? void 0 : exercise.id,
532
534
  });
@@ -542,11 +544,11 @@ function pickTrainerExercise(params) {
542
544
  }
543
545
  const CARDIO_TRACE_INDEX_BEFORE_TEMPLATE = -1;
544
546
  exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS = 600;
545
- /** Inserts optional cardio before or after template exercises for one routine. */
546
- const attachOptionalCardioToRoutine = ({ cardioPreference, sortedExercises, criteria, debugExerciseSelectionTrace, exerciseSelectionTraces, routine, templatePrescriptionCount, routineExercises, }) => {
547
+ /** Inserts optional cardio before or after template exercises for one workout template. */
548
+ const attachOptionalCardioToWorkoutTemplate = ({ cardioPreference, sortedExercises, criteria, debugExerciseSelectionTrace, exerciseSelectionTraces, workoutTemplate, templatePrescriptionCount, workoutExercises, }) => {
547
549
  if (cardioPreference === 'no-cardio')
548
550
  return;
549
- // Cardio is reusable across routines, so program/routine used-id contexts are
551
+ // Cardio is reusable across workout templates, so program/workoutTemplate used-id contexts are
550
552
  // intentionally omitted — only the global excluded set applies.
551
553
  const selectionParams = {
552
554
  sortedExercises,
@@ -557,7 +559,7 @@ const attachOptionalCardioToRoutine = ({ cardioPreference, sortedExercises, crit
557
559
  equipments: criteria.equipments,
558
560
  muscleGroup: 'cardio',
559
561
  exerciseCategory: 'all',
560
- routineBarbellExerciseCount: 0,
562
+ workoutBarbellExerciseCount: 0,
561
563
  },
562
564
  context: { excludedExerciseIds: criteria.excludedExerciseIds },
563
565
  };
@@ -567,17 +569,18 @@ const attachOptionalCardioToRoutine = ({ cardioPreference, sortedExercises, crit
567
569
  return;
568
570
  const slot = {
569
571
  kind: 'cardio',
570
- exerciseTemplate: cardioExercise,
571
- durationSeconds: exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS,
572
+ exercise_template: cardioExercise,
573
+ duration_seconds: exports.DEFAULT_TRAINER_CARDIO_ATTACHMENT_DURATION_SECONDS,
574
+ set_count: 1,
572
575
  };
573
576
  if (cardioPreference === 'workout-start')
574
- routineExercises.unshift(slot);
577
+ workoutExercises.unshift(slot);
575
578
  else
576
- routineExercises.push(slot);
579
+ workoutExercises.push(slot);
577
580
  if (debugExerciseSelectionTrace) {
578
581
  exerciseSelectionTraces.push({
579
582
  traceKind: 'cardio',
580
- routine,
583
+ workoutTemplate,
581
584
  prescriptionIndex: cardioPreference === 'workout-start'
582
585
  ? CARDIO_TRACE_INDEX_BEFORE_TEMPLATE
583
586
  : templatePrescriptionCount,
@@ -592,28 +595,27 @@ function generateProgram(params) {
592
595
  var _a, _b;
593
596
  const { trainerAlgorithmSettings, frequency, goal, level, equipments, workoutDurationMinutes, restTimerLength, exerciseStore, focusMuscle, excludedExerciseIds, debugExerciseSelectionTrace, cardioPreference, } = params;
594
597
  const exerciseSelectionTraces = [];
595
- const routines = exports.programSplits[frequency];
598
+ const workoutTemplates = exports.programSplits[frequency];
596
599
  const program = {
597
- name: frequency,
598
- routines: [],
600
+ workoutTemplates: [],
599
601
  };
600
602
  // TODO: Rename sortedExercises to sortedExercisesByMuscleGroup or something similar
601
603
  const sortedExercises = (0, exports.getPrioritySortedExercises)(trainerAlgorithmSettings.exercise_priorities, exerciseStore);
602
604
  const programUsedExerciseIds = new Set();
603
605
  let programFocusMuscleExerciseCount = 0;
604
606
  const allErrors = [];
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];
607
+ for (const workout of workoutTemplates) {
608
+ const workoutTemplate = trainerAlgorithmSettings.templates[workout];
609
+ let workoutBarbellExerciseCount = 0;
610
+ const workoutUsedExerciseIds = new Set();
611
+ const workoutExercises = [];
612
+ for (let prescriptionIndex = 0; prescriptionIndex < workoutTemplate.exercises.length; prescriptionIndex++) {
613
+ const exercisePrescription = workoutTemplate.exercises[prescriptionIndex];
612
614
  const muscleGroup = getMuscleGroup({
613
615
  muscleGroupPrescription: exercisePrescription.muscle_group,
614
616
  programFocusMuscleExerciseCount,
615
617
  focusMuscle,
616
- routineName: routine,
618
+ workoutTemplateName: workout,
617
619
  });
618
620
  // If the muscle group is not found, skip the exercise
619
621
  if (!muscleGroup) {
@@ -632,7 +634,7 @@ function generateProgram(params) {
632
634
  exerciseCategory: exercisePrescription.category,
633
635
  equipments,
634
636
  muscleGroup,
635
- routineBarbellExerciseCount,
637
+ workoutBarbellExerciseCount,
636
638
  level,
637
639
  goal,
638
640
  };
@@ -643,7 +645,7 @@ function generateProgram(params) {
643
645
  criteria,
644
646
  context: {
645
647
  programUsedExerciseIds,
646
- routineUsedExerciseIds,
648
+ workoutUsedExerciseIds,
647
649
  excludedExerciseIds,
648
650
  },
649
651
  withTrace: true,
@@ -651,7 +653,7 @@ function generateProgram(params) {
651
653
  exercise = selection.exercise;
652
654
  exerciseSelectionTraces.push({
653
655
  traceKind: 'template',
654
- routine,
656
+ workoutTemplate: workout,
655
657
  prescriptionIndex,
656
658
  prescription: exercisePrescription,
657
659
  resolvedMuscleGroup: muscleGroup,
@@ -666,30 +668,29 @@ function generateProgram(params) {
666
668
  criteria,
667
669
  context: {
668
670
  programUsedExerciseIds,
669
- routineUsedExerciseIds,
671
+ workoutUsedExerciseIds,
670
672
  excludedExerciseIds,
671
673
  },
672
674
  });
673
675
  }
674
676
  if (!!exercise) {
675
677
  programUsedExerciseIds.add(exercise.id);
676
- routineUsedExerciseIds.add(exercise.id);
678
+ workoutUsedExerciseIds.add(exercise.id);
677
679
  if (exercise.equipment_category === 'barbell')
678
- routineBarbellExerciseCount++;
680
+ workoutBarbellExerciseCount++;
679
681
  if (exercisePrescription.muscle_group === 'focus_muscle')
680
682
  programFocusMuscleExerciseCount++;
681
683
  const repRange = (0, exports.getTrainerRepRange)(trainerAlgorithmSettings, goal, exercisePrescription.category);
682
- routineExercises.push({
684
+ workoutExercises.push({
683
685
  kind: 'resistance',
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 : '',
686
+ exercise_template: exercise,
687
+ set_count: (0, exports.getTrainerSetCount)(trainerAlgorithmSettings, goal, frequency),
688
+ warmup_set_count: (_b = exercisePrescription.warmup_set_count) !== null && _b !== void 0 ? _b : 0,
689
+ rep_range: {
690
+ start: repRange.rep_range_start,
691
+ end: repRange.rep_range_end,
692
+ },
693
+ rest_seconds: (0, exports.getTrainerRestTimerSeconds)(trainerAlgorithmSettings, goal, restTimerLength, exercisePrescription.category),
693
694
  });
694
695
  }
695
696
  else {
@@ -707,23 +708,21 @@ function generateProgram(params) {
707
708
  });
708
709
  }
709
710
  }
710
- attachOptionalCardioToRoutine({
711
+ attachOptionalCardioToWorkoutTemplate({
711
712
  cardioPreference,
712
713
  sortedExercises,
713
714
  criteria: { equipments, level, goal, frequency, excludedExerciseIds },
714
715
  debugExerciseSelectionTrace,
715
716
  exerciseSelectionTraces,
716
- routine,
717
- templatePrescriptionCount: routineTemplate.exercises.length,
718
- routineExercises,
717
+ workoutTemplate: workout,
718
+ templatePrescriptionCount: workoutTemplate.exercises.length,
719
+ workoutExercises,
719
720
  });
720
- program.routines.push({
721
- name: routine,
722
- notes: routineTemplate.notes,
723
- exercises: routineExercises,
721
+ program.workoutTemplates.push({
722
+ name: workout,
723
+ exercises: workoutExercises,
724
724
  });
725
725
  }
726
- // Return result based on whether there were errors
727
726
  if (allErrors.length > 0) {
728
727
  const result = {
729
728
  success: false,