@aneuhold/core-ts-db-lib 4.0.3 → 4.1.0

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.
Files changed (151) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/lib/browser.d.ts +36 -4
  3. package/lib/browser.d.ts.map +1 -1
  4. package/lib/browser.js +23 -2
  5. package/lib/browser.js.map +1 -1
  6. package/lib/browser.ts +126 -4
  7. package/lib/documents/BaseDocument.d.ts +8 -0
  8. package/lib/documents/BaseDocument.d.ts.map +1 -1
  9. package/lib/documents/BaseDocument.js +10 -0
  10. package/lib/documents/BaseDocument.js.map +1 -1
  11. package/lib/documents/BaseDocument.ts +18 -0
  12. package/lib/documents/common/User.d.ts +1 -0
  13. package/lib/documents/common/User.d.ts.map +1 -1
  14. package/lib/documents/common/User.js +4 -2
  15. package/lib/documents/common/User.js.map +1 -1
  16. package/lib/documents/common/User.ts +4 -2
  17. package/lib/documents/dashboard/NonogramKatanaItem.d.ts +1 -1
  18. package/lib/documents/dashboard/NonogramKatanaItem.js +1 -1
  19. package/lib/documents/dashboard/NonogramKatanaItem.js.map +1 -1
  20. package/lib/documents/dashboard/NonogramKatanaItem.ts +1 -1
  21. package/lib/documents/dashboard/NonogramKatanaUpgrade.d.ts +1 -1
  22. package/lib/documents/dashboard/NonogramKatanaUpgrade.js +1 -1
  23. package/lib/documents/dashboard/NonogramKatanaUpgrade.js.map +1 -1
  24. package/lib/documents/dashboard/NonogramKatanaUpgrade.ts +1 -1
  25. package/lib/documents/dashboard/Task.d.ts +4 -4
  26. package/lib/documents/dashboard/UserConfig.d.ts +4 -4
  27. package/lib/documents/workout/README.md +557 -0
  28. package/lib/documents/workout/WorkoutEquipmentType.d.ts +22 -0
  29. package/lib/documents/workout/WorkoutEquipmentType.d.ts.map +1 -0
  30. package/lib/documents/workout/WorkoutEquipmentType.js +31 -0
  31. package/lib/documents/workout/WorkoutEquipmentType.js.map +1 -0
  32. package/lib/documents/workout/WorkoutEquipmentType.ts +40 -0
  33. package/lib/documents/workout/WorkoutExercise.d.ts +82 -0
  34. package/lib/documents/workout/WorkoutExercise.d.ts.map +1 -0
  35. package/lib/documents/workout/WorkoutExercise.js +124 -0
  36. package/lib/documents/workout/WorkoutExercise.js.map +1 -0
  37. package/lib/documents/workout/WorkoutExercise.ts +143 -0
  38. package/lib/documents/workout/WorkoutExerciseCalibration.d.ts +43 -0
  39. package/lib/documents/workout/WorkoutExerciseCalibration.d.ts.map +1 -0
  40. package/lib/documents/workout/WorkoutExerciseCalibration.js +45 -0
  41. package/lib/documents/workout/WorkoutExerciseCalibration.js.map +1 -0
  42. package/lib/documents/workout/WorkoutExerciseCalibration.ts +74 -0
  43. package/lib/documents/workout/WorkoutMesocycle.d.ts +49 -0
  44. package/lib/documents/workout/WorkoutMesocycle.d.ts.map +1 -0
  45. package/lib/documents/workout/WorkoutMesocycle.js +84 -0
  46. package/lib/documents/workout/WorkoutMesocycle.js.map +1 -0
  47. package/lib/documents/workout/WorkoutMesocycle.ts +102 -0
  48. package/lib/documents/workout/WorkoutMicrocycle.d.ts +27 -0
  49. package/lib/documents/workout/WorkoutMicrocycle.d.ts.map +1 -0
  50. package/lib/documents/workout/WorkoutMicrocycle.js +42 -0
  51. package/lib/documents/workout/WorkoutMicrocycle.js.map +1 -0
  52. package/lib/documents/workout/WorkoutMicrocycle.ts +55 -0
  53. package/lib/documents/workout/WorkoutMuscleGroup.d.ts +22 -0
  54. package/lib/documents/workout/WorkoutMuscleGroup.d.ts.map +1 -0
  55. package/lib/documents/workout/WorkoutMuscleGroup.js +25 -0
  56. package/lib/documents/workout/WorkoutMuscleGroup.js.map +1 -0
  57. package/lib/documents/workout/WorkoutMuscleGroup.ts +34 -0
  58. package/lib/documents/workout/WorkoutSession.d.ts +39 -0
  59. package/lib/documents/workout/WorkoutSession.d.ts.map +1 -0
  60. package/lib/documents/workout/WorkoutSession.js +66 -0
  61. package/lib/documents/workout/WorkoutSession.js.map +1 -0
  62. package/lib/documents/workout/WorkoutSession.ts +79 -0
  63. package/lib/documents/workout/WorkoutSessionExercise.d.ts +39 -0
  64. package/lib/documents/workout/WorkoutSessionExercise.d.ts.map +1 -0
  65. package/lib/documents/workout/WorkoutSessionExercise.js +66 -0
  66. package/lib/documents/workout/WorkoutSessionExercise.js.map +1 -0
  67. package/lib/documents/workout/WorkoutSessionExercise.ts +79 -0
  68. package/lib/documents/workout/WorkoutSet.d.ts +41 -0
  69. package/lib/documents/workout/WorkoutSet.d.ts.map +1 -0
  70. package/lib/documents/workout/WorkoutSet.js +69 -0
  71. package/lib/documents/workout/WorkoutSet.js.map +1 -0
  72. package/lib/documents/workout/WorkoutSet.ts +90 -0
  73. package/lib/embedded-types/dashboard/task/FilterSettings.d.ts +6 -6
  74. package/lib/embedded-types/dashboard/task/FilterSettings.d.ts.map +1 -1
  75. package/lib/embedded-types/dashboard/task/FilterSettings.js +4 -2
  76. package/lib/embedded-types/dashboard/task/FilterSettings.js.map +1 -1
  77. package/lib/embedded-types/dashboard/task/FilterSettings.ts +5 -3
  78. package/lib/embedded-types/dashboard/task/SortSettings.d.ts +6 -6
  79. package/lib/embedded-types/dashboard/task/SortSettings.js +1 -1
  80. package/lib/embedded-types/dashboard/task/SortSettings.js.map +1 -1
  81. package/lib/embedded-types/dashboard/task/SortSettings.ts +1 -1
  82. package/lib/embedded-types/workout/Fatigue.d.ts +16 -0
  83. package/lib/embedded-types/workout/Fatigue.d.ts.map +1 -0
  84. package/lib/embedded-types/workout/Fatigue.js +34 -0
  85. package/lib/embedded-types/workout/Fatigue.js.map +1 -0
  86. package/lib/embedded-types/workout/Fatigue.ts +41 -0
  87. package/lib/embedded-types/workout/Rsm.d.ts +17 -0
  88. package/lib/embedded-types/workout/Rsm.d.ts.map +1 -0
  89. package/lib/embedded-types/workout/Rsm.js +34 -0
  90. package/lib/embedded-types/workout/Rsm.js.map +1 -0
  91. package/lib/embedded-types/workout/Rsm.ts +42 -0
  92. package/lib/services/DocumentService.d.ts +19 -0
  93. package/lib/services/DocumentService.d.ts.map +1 -1
  94. package/lib/services/DocumentService.js.map +1 -1
  95. package/lib/services/DocumentService.ts +20 -0
  96. package/lib/services/workout/EquipmentType/WorkoutEquipmentTypeService.d.ts +35 -0
  97. package/lib/services/workout/EquipmentType/WorkoutEquipmentTypeService.d.ts.map +1 -0
  98. package/lib/services/workout/EquipmentType/WorkoutEquipmentTypeService.js +74 -0
  99. package/lib/services/workout/EquipmentType/WorkoutEquipmentTypeService.js.map +1 -0
  100. package/lib/services/workout/EquipmentType/WorkoutEquipmentTypeService.ts +90 -0
  101. package/lib/services/workout/Exercise/WorkoutExerciseService.d.ts +55 -0
  102. package/lib/services/workout/Exercise/WorkoutExerciseService.d.ts.map +1 -0
  103. package/lib/services/workout/Exercise/WorkoutExerciseService.js +133 -0
  104. package/lib/services/workout/Exercise/WorkoutExerciseService.js.map +1 -0
  105. package/lib/services/workout/Exercise/WorkoutExerciseService.ts +173 -0
  106. package/lib/services/workout/ExerciseCalibration/WorkoutExerciseCalibrationService.d.ts +35 -0
  107. package/lib/services/workout/ExerciseCalibration/WorkoutExerciseCalibrationService.d.ts.map +1 -0
  108. package/lib/services/workout/ExerciseCalibration/WorkoutExerciseCalibrationService.js +42 -0
  109. package/lib/services/workout/ExerciseCalibration/WorkoutExerciseCalibrationService.js.map +1 -0
  110. package/lib/services/workout/ExerciseCalibration/WorkoutExerciseCalibrationService.ts +45 -0
  111. package/lib/services/workout/Mesocycle/WorkoutMesocyclePlanContext.d.ts +90 -0
  112. package/lib/services/workout/Mesocycle/WorkoutMesocyclePlanContext.d.ts.map +1 -0
  113. package/lib/services/workout/Mesocycle/WorkoutMesocyclePlanContext.js +131 -0
  114. package/lib/services/workout/Mesocycle/WorkoutMesocyclePlanContext.js.map +1 -0
  115. package/lib/services/workout/Mesocycle/WorkoutMesocyclePlanContext.ts +159 -0
  116. package/lib/services/workout/Mesocycle/WorkoutMesocycleService.d.ts +52 -0
  117. package/lib/services/workout/Mesocycle/WorkoutMesocycleService.d.ts.map +1 -0
  118. package/lib/services/workout/Mesocycle/WorkoutMesocycleService.js +180 -0
  119. package/lib/services/workout/Mesocycle/WorkoutMesocycleService.js.map +1 -0
  120. package/lib/services/workout/Mesocycle/WorkoutMesocycleService.ts +274 -0
  121. package/lib/services/workout/Microcycle/WorkoutMicrocycleService.d.ts +28 -0
  122. package/lib/services/workout/Microcycle/WorkoutMicrocycleService.d.ts.map +1 -0
  123. package/lib/services/workout/Microcycle/WorkoutMicrocycleService.js +172 -0
  124. package/lib/services/workout/Microcycle/WorkoutMicrocycleService.js.map +1 -0
  125. package/lib/services/workout/Microcycle/WorkoutMicrocycleService.ts +236 -0
  126. package/lib/services/workout/Session/WorkoutSessionService.d.ts +49 -0
  127. package/lib/services/workout/Session/WorkoutSessionService.d.ts.map +1 -0
  128. package/lib/services/workout/Session/WorkoutSessionService.js +95 -0
  129. package/lib/services/workout/Session/WorkoutSessionService.js.map +1 -0
  130. package/lib/services/workout/Session/WorkoutSessionService.ts +136 -0
  131. package/lib/services/workout/SessionExercise/WorkoutSessionExerciseService.d.ts +45 -0
  132. package/lib/services/workout/SessionExercise/WorkoutSessionExerciseService.d.ts.map +1 -0
  133. package/lib/services/workout/SessionExercise/WorkoutSessionExerciseService.js +69 -0
  134. package/lib/services/workout/SessionExercise/WorkoutSessionExerciseService.js.map +1 -0
  135. package/lib/services/workout/SessionExercise/WorkoutSessionExerciseService.ts +77 -0
  136. package/lib/services/workout/Set/WorkoutSetService.d.ts +36 -0
  137. package/lib/services/workout/Set/WorkoutSetService.d.ts.map +1 -0
  138. package/lib/services/workout/Set/WorkoutSetService.js +90 -0
  139. package/lib/services/workout/Set/WorkoutSetService.js.map +1 -0
  140. package/lib/services/workout/Set/WorkoutSetService.ts +153 -0
  141. package/lib/services/workout/util/SFR/WorkoutSFRService.d.ts +29 -0
  142. package/lib/services/workout/util/SFR/WorkoutSFRService.d.ts.map +1 -0
  143. package/lib/services/workout/util/SFR/WorkoutSFRService.js +50 -0
  144. package/lib/services/workout/util/SFR/WorkoutSFRService.js.map +1 -0
  145. package/lib/services/workout/util/SFR/WorkoutSFRService.ts +61 -0
  146. package/lib/services/workout/util/VolumePlanning/WorkoutVolumePlanningService.d.ts +48 -0
  147. package/lib/services/workout/util/VolumePlanning/WorkoutVolumePlanningService.d.ts.map +1 -0
  148. package/lib/services/workout/util/VolumePlanning/WorkoutVolumePlanningService.js +261 -0
  149. package/lib/services/workout/util/VolumePlanning/WorkoutVolumePlanningService.js.map +1 -0
  150. package/lib/services/workout/util/VolumePlanning/WorkoutVolumePlanningService.ts +339 -0
  151. package/package.json +5 -4
@@ -0,0 +1,77 @@
1
+ import type { WorkoutSessionExercise } from '../../../documents/workout/WorkoutSessionExercise.js';
2
+ import WorkoutSFRService from '../util/SFR/WorkoutSFRService.js';
3
+
4
+ /**
5
+ * A service for handling operations related to {@link WorkoutSessionExercise}s.
6
+ */
7
+ export default class WorkoutSessionExerciseService {
8
+ /**
9
+ * Calculates the total Raw Stimulus Magnitude for a specific exercise within a session.
10
+ *
11
+ * @param sessionExercise The workout session exercise.
12
+ */
13
+ static getRsmTotal(sessionExercise: WorkoutSessionExercise): number | null {
14
+ return WorkoutSFRService.getRsmTotal(sessionExercise.rsm);
15
+ }
16
+
17
+ /**
18
+ * Calculates the total fatigue score for a specific exercise within a session.
19
+ *
20
+ * @param sessionExercise The workout session exercise.
21
+ */
22
+ static getFatigueTotal(sessionExercise: WorkoutSessionExercise): number | null {
23
+ return WorkoutSFRService.getFatigueTotal(sessionExercise.fatigue);
24
+ }
25
+
26
+ /**
27
+ * Calculates the Stimulus to Fatigue Ratio (SFR) for a specific exercise.
28
+ *
29
+ * @param sessionExercise The workout session exercise.
30
+ */
31
+ static getSFR(sessionExercise: WorkoutSessionExercise): number | null {
32
+ return WorkoutSFRService.getSFR(sessionExercise.rsm, sessionExercise.fatigue);
33
+ }
34
+
35
+ /**
36
+ * Uses the soreness/performance table from the workout model notes to recommend whether to add
37
+ * sets next microcycle or employ recovery sessions.
38
+ *
39
+ * Interpretation:
40
+ * - Returns `-1` when recovery sessions should be employed.
41
+ * - Returns `0` when no sets should be added.
42
+ * - Returns a non-negative integer when sets should be added.
43
+ * - Returns `null` when insufficient data is available.
44
+ *
45
+ * The table is:
46
+ *
47
+ * | Soreness Score ↓ \ Performance Score → | 0 | 1 | 2 | 3 |
48
+ |---|---|---|---|---|
49
+ | **0** | Add 1–3 sets | Add 0–2 sets | Do not add sets | Employ recovery sessions (see Fatigue Management) |
50
+ | **1** | Add 1–2 sets | Add 0–1 sets | Do not add sets | Employ recovery sessions (see Fatigue Management) |
51
+ | **2** | Do not add sets | Do not add sets | Do not add sets | Employ recovery sessions (see Fatigue Management) |
52
+ | **3** | Do not add sets | Do not add sets | Do not add sets | Employ recovery sessions (see Fatigue Management) |
53
+ */
54
+ static getRecommendedSetAdditionsOrRecovery(
55
+ workoutSessionExercise: WorkoutSessionExercise
56
+ ): number | null {
57
+ const { performanceScore, sorenessScore } = workoutSessionExercise;
58
+ if (sorenessScore == null || performanceScore == null) {
59
+ return null;
60
+ }
61
+
62
+ // Table mapping (sorenessScore rows, performanceScore columns).
63
+ // Values are representative set additions (midpoint of table ranges), or -1 for recovery.
64
+ const table: number[][] = [
65
+ // Soreness 0: [Add 1-3, Add 0-2, Do not add, Recovery]
66
+ [2, 1, 0, -1],
67
+ // Soreness 1: [Add 1-2, Add 0-1, Do not add, Recovery]
68
+ [1, 0, 0, -1],
69
+ // Soreness 2: [Do not add, Do not add, Do not add, Recovery]
70
+ [0, 0, 0, -1],
71
+ // Soreness 3: [Do not add, Do not add, Do not add, Recovery]
72
+ [0, 0, 0, -1]
73
+ ];
74
+
75
+ return table[sorenessScore]?.[performanceScore] ?? null;
76
+ }
77
+ }
@@ -0,0 +1,36 @@
1
+ import type { WorkoutExercise } from '../../../documents/workout/WorkoutExercise.js';
2
+ import type { WorkoutExerciseCalibration } from '../../../documents/workout/WorkoutExerciseCalibration.js';
3
+ import type { WorkoutSession } from '../../../documents/workout/WorkoutSession.js';
4
+ import type { WorkoutSessionExercise } from '../../../documents/workout/WorkoutSessionExercise.js';
5
+ import type WorkoutMesocyclePlanContext from '../Mesocycle/WorkoutMesocyclePlanContext.js';
6
+ export default class WorkoutSetService {
7
+ /**
8
+ * Generates a list of workout sets for a given session exercise based on progression logic.
9
+ *
10
+ * This handles the "micro" decisions of load selection:
11
+ * - Calculating the initial target weight/reps based on microcycle progression.
12
+ * - Handling intra-session fatigue (dropping reps/weight across sets).
13
+ * - Applying Deload phase modifications (cutting volume/intensity).
14
+ */
15
+ static generateSetsForSessionExercise({ context, exercise, calibration, session, sessionExercise, microcycleIndex, sessionIndex, setCount, targetRir, isDeloadMicrocycle }: {
16
+ context: WorkoutMesocyclePlanContext;
17
+ exercise: WorkoutExercise;
18
+ calibration: WorkoutExerciseCalibration;
19
+ session: WorkoutSession;
20
+ sessionExercise: WorkoutSessionExercise;
21
+ microcycleIndex: number;
22
+ sessionIndex: number;
23
+ setCount: number;
24
+ targetRir: number;
25
+ isDeloadMicrocycle: boolean;
26
+ }): void;
27
+ /**
28
+ * Generates the planned reps and weight for a specific set within a session exercise, only
29
+ * taking into account simple -2 reps drop per set logic, and deload modifications.
30
+ *
31
+ * This needs to be checked with the source material to see if it needs to be adjusted based
32
+ * on actual reps performed in previous sets.
33
+ */
34
+ private static generateSetRepsAndWeight;
35
+ }
36
+ //# sourceMappingURL=WorkoutSetService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkoutSetService.d.ts","sourceRoot":"","sources":["../../../../src/services/workout/Set/WorkoutSetService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,eAAe,EAChB,MAAM,+CAA+C,CAAC;AACvD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0DAA0D,CAAC;AAC3G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8CAA8C,CAAC;AACnF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sDAAsD,CAAC;AAInG,OAAO,KAAK,2BAA2B,MAAM,6CAA6C,CAAC;AAE3F,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC;;;;;;;OAOG;IACH,MAAM,CAAC,8BAA8B,CAAC,EACpC,OAAO,EACP,QAAQ,EACR,WAAW,EACX,OAAO,EACP,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,kBAAkB,EACnB,EAAE;QACD,OAAO,EAAE,2BAA2B,CAAC;QACrC,QAAQ,EAAE,eAAe,CAAC;QAC1B,WAAW,EAAE,0BAA0B,CAAC;QACxC,OAAO,EAAE,cAAc,CAAC;QACxB,eAAe,EAAE,sBAAsB,CAAC;QACxC,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,kBAAkB,EAAE,OAAO,CAAC;KAC7B,GAAG,IAAI;IAoDR;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,wBAAwB;CAiDxC"}
@@ -0,0 +1,90 @@
1
+ import { WorkoutSetSchema } from '../../../documents/workout/WorkoutSet.js';
2
+ import WorkoutEquipmentTypeService from '../EquipmentType/WorkoutEquipmentTypeService.js';
3
+ import WorkoutExerciseService from '../Exercise/WorkoutExerciseService.js';
4
+ export default class WorkoutSetService {
5
+ /**
6
+ * Generates a list of workout sets for a given session exercise based on progression logic.
7
+ *
8
+ * This handles the "micro" decisions of load selection:
9
+ * - Calculating the initial target weight/reps based on microcycle progression.
10
+ * - Handling intra-session fatigue (dropping reps/weight across sets).
11
+ * - Applying Deload phase modifications (cutting volume/intensity).
12
+ */
13
+ static generateSetsForSessionExercise({ context, exercise, calibration, session, sessionExercise, microcycleIndex, sessionIndex, setCount, targetRir, isDeloadMicrocycle }) {
14
+ const equipment = context.equipmentMap.get(exercise.workoutEquipmentTypeId);
15
+ if (!equipment) {
16
+ throw new Error(`Equipment type not found for exercise ${exercise._id}, ${exercise.exerciseName}`);
17
+ }
18
+ const sets = [];
19
+ // Calculate progressed targets for the first set
20
+ const { targetWeight: firstSetWeight, targetReps: firstSetReps } = WorkoutExerciseService.calculateTargetRepsAndWeightForFirstSet({
21
+ exercise,
22
+ calibration,
23
+ equipment,
24
+ microcycleIndex,
25
+ firstMicrocycleRir: context.FIRST_MICROCYCLE_RIR
26
+ });
27
+ for (let setIndex = 0; setIndex < setCount; setIndex++) {
28
+ const { plannedReps, plannedWeight } = this.generateSetRepsAndWeight(
29
+ // If there is a previous set, base off that, otherwise use first set targets
30
+ sets[setIndex - 1]?.plannedReps || firstSetReps, sets[setIndex - 1]?.plannedWeight || firstSetWeight, setIndex, exercise.repRange, equipment, {
31
+ isDeloadMicrocycle,
32
+ sessionIndex,
33
+ plannedSessionCountPerMicrocycle: context.mesocycle.plannedSessionCountPerMicrocycle
34
+ });
35
+ const workoutSet = WorkoutSetSchema.parse({
36
+ userId: exercise.userId,
37
+ workoutExerciseId: exercise._id,
38
+ workoutSessionId: session._id,
39
+ workoutSessionExerciseId: sessionExercise._id,
40
+ plannedReps,
41
+ plannedWeight,
42
+ plannedRir: targetRir,
43
+ exerciseProperties: calibration.exerciseProperties
44
+ });
45
+ sets.push(workoutSet);
46
+ }
47
+ context.setsToCreate.push(...sets);
48
+ }
49
+ /**
50
+ * Generates the planned reps and weight for a specific set within a session exercise, only
51
+ * taking into account simple -2 reps drop per set logic, and deload modifications.
52
+ *
53
+ * This needs to be checked with the source material to see if it needs to be adjusted based
54
+ * on actual reps performed in previous sets.
55
+ */
56
+ static generateSetRepsAndWeight(firstSetOrPreviousSetReps, firstSetOrPreviousSetWeight, setIndex, repRange, equipment, deloadInfo) {
57
+ const repRangeValues = WorkoutExerciseService.getRepRangeValues(repRange);
58
+ let currentReps = firstSetOrPreviousSetReps;
59
+ let currentWeight = firstSetOrPreviousSetWeight;
60
+ // Ideally, drop 2 reps per set within the session (19 -> 17 -> 15, etc.)
61
+ // But if that would go below the min reps, keep it at min reps.
62
+ if (firstSetOrPreviousSetReps - 2 < repRangeValues.min && setIndex > 0) {
63
+ // Reduce weight by 2% using the same technique as progression
64
+ const twoPercentDecrease = currentWeight / 1.02;
65
+ const reducedWeight = WorkoutEquipmentTypeService.findNearestWeight(equipment, twoPercentDecrease, 'down');
66
+ if (reducedWeight !== null) {
67
+ currentWeight = reducedWeight;
68
+ }
69
+ else if (firstSetOrPreviousSetReps - 2 > 5) {
70
+ // If we can't reduce weight, but we can reduce reps without going too low,
71
+ // then do that.
72
+ currentReps = firstSetOrPreviousSetReps - 2;
73
+ }
74
+ }
75
+ else if (setIndex > 0) {
76
+ currentReps = firstSetOrPreviousSetReps - 2;
77
+ }
78
+ // Apply deload modifications, but only if the set is the first in the session
79
+ if (deloadInfo.isDeloadMicrocycle && setIndex === 0) {
80
+ currentReps = Math.floor(firstSetOrPreviousSetReps / 2);
81
+ // First half of deload microcycle: same weight, half reps/sets
82
+ // Second half: half weight too
83
+ if (deloadInfo.sessionIndex >= Math.floor(deloadInfo.plannedSessionCountPerMicrocycle / 2)) {
84
+ currentWeight = Math.floor(currentWeight / 2);
85
+ }
86
+ }
87
+ return { plannedReps: currentReps, plannedWeight: currentWeight };
88
+ }
89
+ }
90
+ //# sourceMappingURL=WorkoutSetService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkoutSetService.js","sourceRoot":"","sources":["../../../../src/services/workout/Set/WorkoutSetService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,153 @@
1
+ import type { WorkoutEquipmentType } from '../../../documents/workout/WorkoutEquipmentType.js';
2
+ import type {
3
+ ExerciseRepRange,
4
+ WorkoutExercise
5
+ } from '../../../documents/workout/WorkoutExercise.js';
6
+ import type { WorkoutExerciseCalibration } from '../../../documents/workout/WorkoutExerciseCalibration.js';
7
+ import type { WorkoutSession } from '../../../documents/workout/WorkoutSession.js';
8
+ import type { WorkoutSessionExercise } from '../../../documents/workout/WorkoutSessionExercise.js';
9
+ import { WorkoutSetSchema, type WorkoutSet } from '../../../documents/workout/WorkoutSet.js';
10
+ import WorkoutEquipmentTypeService from '../EquipmentType/WorkoutEquipmentTypeService.js';
11
+ import WorkoutExerciseService from '../Exercise/WorkoutExerciseService.js';
12
+ import type WorkoutMesocyclePlanContext from '../Mesocycle/WorkoutMesocyclePlanContext.js';
13
+
14
+ export default class WorkoutSetService {
15
+ /**
16
+ * Generates a list of workout sets for a given session exercise based on progression logic.
17
+ *
18
+ * This handles the "micro" decisions of load selection:
19
+ * - Calculating the initial target weight/reps based on microcycle progression.
20
+ * - Handling intra-session fatigue (dropping reps/weight across sets).
21
+ * - Applying Deload phase modifications (cutting volume/intensity).
22
+ */
23
+ static generateSetsForSessionExercise({
24
+ context,
25
+ exercise,
26
+ calibration,
27
+ session,
28
+ sessionExercise,
29
+ microcycleIndex,
30
+ sessionIndex,
31
+ setCount,
32
+ targetRir,
33
+ isDeloadMicrocycle
34
+ }: {
35
+ context: WorkoutMesocyclePlanContext;
36
+ exercise: WorkoutExercise;
37
+ calibration: WorkoutExerciseCalibration;
38
+ session: WorkoutSession;
39
+ sessionExercise: WorkoutSessionExercise;
40
+ microcycleIndex: number;
41
+ sessionIndex: number;
42
+ setCount: number;
43
+ targetRir: number;
44
+ isDeloadMicrocycle: boolean;
45
+ }): void {
46
+ const equipment = context.equipmentMap.get(exercise.workoutEquipmentTypeId);
47
+ if (!equipment) {
48
+ throw new Error(
49
+ `Equipment type not found for exercise ${exercise._id}, ${exercise.exerciseName}`
50
+ );
51
+ }
52
+
53
+ const sets: WorkoutSet[] = [];
54
+
55
+ // Calculate progressed targets for the first set
56
+ const { targetWeight: firstSetWeight, targetReps: firstSetReps } =
57
+ WorkoutExerciseService.calculateTargetRepsAndWeightForFirstSet({
58
+ exercise,
59
+ calibration,
60
+ equipment,
61
+ microcycleIndex,
62
+ firstMicrocycleRir: context.FIRST_MICROCYCLE_RIR
63
+ });
64
+
65
+ for (let setIndex = 0; setIndex < setCount; setIndex++) {
66
+ const { plannedReps, plannedWeight } = this.generateSetRepsAndWeight(
67
+ // If there is a previous set, base off that, otherwise use first set targets
68
+ sets[setIndex - 1]?.plannedReps || firstSetReps,
69
+ sets[setIndex - 1]?.plannedWeight || firstSetWeight,
70
+ setIndex,
71
+ exercise.repRange,
72
+ equipment,
73
+ {
74
+ isDeloadMicrocycle,
75
+ sessionIndex,
76
+ plannedSessionCountPerMicrocycle: context.mesocycle.plannedSessionCountPerMicrocycle
77
+ }
78
+ );
79
+
80
+ const workoutSet = WorkoutSetSchema.parse({
81
+ userId: exercise.userId,
82
+ workoutExerciseId: exercise._id,
83
+ workoutSessionId: session._id,
84
+ workoutSessionExerciseId: sessionExercise._id,
85
+ plannedReps,
86
+ plannedWeight,
87
+ plannedRir: targetRir,
88
+ exerciseProperties: calibration.exerciseProperties
89
+ });
90
+
91
+ sets.push(workoutSet);
92
+ }
93
+
94
+ context.setsToCreate.push(...sets);
95
+ }
96
+
97
+ /**
98
+ * Generates the planned reps and weight for a specific set within a session exercise, only
99
+ * taking into account simple -2 reps drop per set logic, and deload modifications.
100
+ *
101
+ * This needs to be checked with the source material to see if it needs to be adjusted based
102
+ * on actual reps performed in previous sets.
103
+ */
104
+ private static generateSetRepsAndWeight(
105
+ firstSetOrPreviousSetReps: number,
106
+ firstSetOrPreviousSetWeight: number,
107
+ setIndex: number,
108
+ repRange: ExerciseRepRange,
109
+ equipment: WorkoutEquipmentType,
110
+ deloadInfo: {
111
+ isDeloadMicrocycle: boolean;
112
+ sessionIndex: number;
113
+ plannedSessionCountPerMicrocycle: number;
114
+ }
115
+ ) {
116
+ const repRangeValues = WorkoutExerciseService.getRepRangeValues(repRange);
117
+ let currentReps = firstSetOrPreviousSetReps;
118
+ let currentWeight = firstSetOrPreviousSetWeight;
119
+
120
+ // Ideally, drop 2 reps per set within the session (19 -> 17 -> 15, etc.)
121
+ // But if that would go below the min reps, keep it at min reps.
122
+ if (firstSetOrPreviousSetReps - 2 < repRangeValues.min && setIndex > 0) {
123
+ // Reduce weight by 2% using the same technique as progression
124
+ const twoPercentDecrease = currentWeight / 1.02;
125
+ const reducedWeight = WorkoutEquipmentTypeService.findNearestWeight(
126
+ equipment,
127
+ twoPercentDecrease,
128
+ 'down'
129
+ );
130
+ if (reducedWeight !== null) {
131
+ currentWeight = reducedWeight;
132
+ } else if (firstSetOrPreviousSetReps - 2 > 5) {
133
+ // If we can't reduce weight, but we can reduce reps without going too low,
134
+ // then do that.
135
+ currentReps = firstSetOrPreviousSetReps - 2;
136
+ }
137
+ } else if (setIndex > 0) {
138
+ currentReps = firstSetOrPreviousSetReps - 2;
139
+ }
140
+
141
+ // Apply deload modifications, but only if the set is the first in the session
142
+ if (deloadInfo.isDeloadMicrocycle && setIndex === 0) {
143
+ currentReps = Math.floor(firstSetOrPreviousSetReps / 2);
144
+ // First half of deload microcycle: same weight, half reps/sets
145
+ // Second half: half weight too
146
+ if (deloadInfo.sessionIndex >= Math.floor(deloadInfo.plannedSessionCountPerMicrocycle / 2)) {
147
+ currentWeight = Math.floor(currentWeight / 2);
148
+ }
149
+ }
150
+
151
+ return { plannedReps: currentReps, plannedWeight: currentWeight };
152
+ }
153
+ }
@@ -0,0 +1,29 @@
1
+ import type { Fatigue } from '../../../../embedded-types/workout/Fatigue.js';
2
+ import type { RSM } from '../../../../embedded-types/workout/Rsm.js';
3
+ /**
4
+ * A service for handling Stimulus to Fatigue Ratio (SFR) calculations.
5
+ *
6
+ * This service works with any object that has RSM and Fatigue data.
7
+ */
8
+ export default class WorkoutSFRService {
9
+ /**
10
+ * Calculates the total Raw Stimulus Magnitude.
11
+ *
12
+ * @param rsm The RSM data.
13
+ */
14
+ static getRsmTotal(rsm: RSM | null | undefined): number | null;
15
+ /**
16
+ * Calculates the total fatigue score.
17
+ *
18
+ * @param fatigue The fatigue data.
19
+ */
20
+ static getFatigueTotal(fatigue: Fatigue | null | undefined): number | null;
21
+ /**
22
+ * Calculates the Stimulus to Fatigue Ratio (SFR).
23
+ *
24
+ * @param rsm The RSM data.
25
+ * @param fatigue The fatigue data.
26
+ */
27
+ static getSFR(rsm: RSM | null | undefined, fatigue: Fatigue | null | undefined): number | null;
28
+ }
29
+ //# sourceMappingURL=WorkoutSFRService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkoutSFRService.d.ts","sourceRoot":"","sources":["../../../../../src/services/workout/util/SFR/WorkoutSFRService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+CAA+C,CAAC;AAC7E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,2CAA2C,CAAC;AAErE;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC;;;;OAIG;IACH,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI;IAO9D;;;;OAIG;IACH,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI;IAc1E;;;;;OAKG;IACH,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI;CAc/F"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * A service for handling Stimulus to Fatigue Ratio (SFR) calculations.
3
+ *
4
+ * This service works with any object that has RSM and Fatigue data.
5
+ */
6
+ export default class WorkoutSFRService {
7
+ /**
8
+ * Calculates the total Raw Stimulus Magnitude.
9
+ *
10
+ * @param rsm The RSM data.
11
+ */
12
+ static getRsmTotal(rsm) {
13
+ if (!rsm || rsm.mindMuscleConnection == null || rsm.pump == null || rsm.disruption == null) {
14
+ return null;
15
+ }
16
+ return rsm.mindMuscleConnection + rsm.pump + rsm.disruption;
17
+ }
18
+ /**
19
+ * Calculates the total fatigue score.
20
+ *
21
+ * @param fatigue The fatigue data.
22
+ */
23
+ static getFatigueTotal(fatigue) {
24
+ if (!fatigue ||
25
+ fatigue.jointAndTissueDisruption == null ||
26
+ fatigue.perceivedEffort == null ||
27
+ fatigue.unusedMusclePerformance == null) {
28
+ return null;
29
+ }
30
+ return (fatigue.jointAndTissueDisruption + fatigue.perceivedEffort + fatigue.unusedMusclePerformance);
31
+ }
32
+ /**
33
+ * Calculates the Stimulus to Fatigue Ratio (SFR).
34
+ *
35
+ * @param rsm The RSM data.
36
+ * @param fatigue The fatigue data.
37
+ */
38
+ static getSFR(rsm, fatigue) {
39
+ const rsmTotal = this.getRsmTotal(rsm);
40
+ const fatigueTotal = this.getFatigueTotal(fatigue);
41
+ if (rsmTotal === null || fatigueTotal === null) {
42
+ return null;
43
+ }
44
+ if (fatigueTotal === 0) {
45
+ return rsmTotal;
46
+ }
47
+ return rsmTotal / fatigueTotal;
48
+ }
49
+ }
50
+ //# sourceMappingURL=WorkoutSFRService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkoutSFRService.js","sourceRoot":"","sources":["../../../../../src/services/workout/util/SFR/WorkoutSFRService.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC;;;;OAIG;IACH,MAAM,CAAC,WAAW,CAAC,GAA2B;QAC5C,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,oBAAoB,IAAI,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;YAC3F,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,GAAG,CAAC,oBAAoB,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC;IAC9D,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,eAAe,CAAC,OAAmC;QACxD,IACE,CAAC,OAAO;YACR,OAAO,CAAC,wBAAwB,IAAI,IAAI;YACxC,OAAO,CAAC,eAAe,IAAI,IAAI;YAC/B,OAAO,CAAC,uBAAuB,IAAI,IAAI,EACvC,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,CACL,OAAO,CAAC,wBAAwB,GAAG,OAAO,CAAC,eAAe,GAAG,OAAO,CAAC,uBAAuB,CAC7F,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,MAAM,CAAC,GAA2B,EAAE,OAAmC;QAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACvC,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAEnD,IAAI,QAAQ,KAAK,IAAI,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;YAC/C,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,OAAO,QAAQ,GAAG,YAAY,CAAC;IACjC,CAAC;CACF"}
@@ -0,0 +1,61 @@
1
+ import type { Fatigue } from '../../../../embedded-types/workout/Fatigue.js';
2
+ import type { RSM } from '../../../../embedded-types/workout/Rsm.js';
3
+
4
+ /**
5
+ * A service for handling Stimulus to Fatigue Ratio (SFR) calculations.
6
+ *
7
+ * This service works with any object that has RSM and Fatigue data.
8
+ */
9
+ export default class WorkoutSFRService {
10
+ /**
11
+ * Calculates the total Raw Stimulus Magnitude.
12
+ *
13
+ * @param rsm The RSM data.
14
+ */
15
+ static getRsmTotal(rsm: RSM | null | undefined): number | null {
16
+ if (!rsm || rsm.mindMuscleConnection == null || rsm.pump == null || rsm.disruption == null) {
17
+ return null;
18
+ }
19
+ return rsm.mindMuscleConnection + rsm.pump + rsm.disruption;
20
+ }
21
+
22
+ /**
23
+ * Calculates the total fatigue score.
24
+ *
25
+ * @param fatigue The fatigue data.
26
+ */
27
+ static getFatigueTotal(fatigue: Fatigue | null | undefined): number | null {
28
+ if (
29
+ !fatigue ||
30
+ fatigue.jointAndTissueDisruption == null ||
31
+ fatigue.perceivedEffort == null ||
32
+ fatigue.unusedMusclePerformance == null
33
+ ) {
34
+ return null;
35
+ }
36
+ return (
37
+ fatigue.jointAndTissueDisruption + fatigue.perceivedEffort + fatigue.unusedMusclePerformance
38
+ );
39
+ }
40
+
41
+ /**
42
+ * Calculates the Stimulus to Fatigue Ratio (SFR).
43
+ *
44
+ * @param rsm The RSM data.
45
+ * @param fatigue The fatigue data.
46
+ */
47
+ static getSFR(rsm: RSM | null | undefined, fatigue: Fatigue | null | undefined): number | null {
48
+ const rsmTotal = this.getRsmTotal(rsm);
49
+ const fatigueTotal = this.getFatigueTotal(fatigue);
50
+
51
+ if (rsmTotal === null || fatigueTotal === null) {
52
+ return null;
53
+ }
54
+
55
+ if (fatigueTotal === 0) {
56
+ return rsmTotal;
57
+ }
58
+
59
+ return rsmTotal / fatigueTotal;
60
+ }
61
+ }
@@ -0,0 +1,48 @@
1
+ import type { UUID } from 'crypto';
2
+ import type WorkoutMesocyclePlanContext from '../../Mesocycle/WorkoutMesocyclePlanContext.js';
3
+ /**
4
+ * A service for handling volume planning operations across microcycles.
5
+ *
6
+ * SCOPE: Microcycle-level volume distribution (calculating set counts per exercise)
7
+ *
8
+ * RESPONSIBILITIES:
9
+ * - Calculate set counts for exercises across a microcycle
10
+ * - Apply progressive overload rules (baseline + historical adjustments)
11
+ * - Handle recovery exercise identification
12
+ * - Enforce volume limits (per exercise, per muscle group per session)
13
+ *
14
+ * RELATED SERVICES:
15
+ * - {@link WorkoutMicrocycleService} - Calls this to get set plans before generating sessions
16
+ * - {@link WorkoutSessionService} - Uses the output to generate actual sessions
17
+ * - {@link WorkoutSessionExerciseService} - Used to calculate SFR and recovery recommendations
18
+ */
19
+ export default class WorkoutVolumePlanningService {
20
+ private static readonly MAX_SETS_PER_EXERCISE;
21
+ private static readonly MAX_SETS_PER_MUSCLE_GROUP_PER_SESSION;
22
+ /**
23
+ * Calculates the set plan for an entire microcycle.
24
+ */
25
+ static calculateSetPlanForMicrocycle(context: WorkoutMesocyclePlanContext, microcycleIndex: number, isDeloadMicrocycle: boolean): {
26
+ exerciseIdToSetCount: Map<UUID, number>;
27
+ recoveryExerciseIds: Set<UUID>;
28
+ };
29
+ /**
30
+ * Calculates the set count for each exercise in a particular muscle group for this microcycle.
31
+ *
32
+ * If there is no previous microcycle data for the muscle group, this falls back to
33
+ * the baseline progression rules.
34
+ */
35
+ private static calculateSetCountForEachExerciseInMuscleGroup;
36
+ /**
37
+ * Calculates the default number of sets for an exercise based on microcycle progression.
38
+ *
39
+ * Key rule: set progression is distributed across exercises that share the same primary muscle group
40
+ * for the entire microcycle, regardless of which session those exercises are in.
41
+ *
42
+ * Baseline: 2 sets per exercise in the muscle group.
43
+ * Progression: add 1 total set per microcycle per muscle group (distributed to earlier exercises
44
+ * in the muscle-group-wide ordering).
45
+ */
46
+ private static calculateBaselineSetCount;
47
+ }
48
+ //# sourceMappingURL=WorkoutVolumePlanningService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkoutVolumePlanningService.d.ts","sourceRoot":"","sources":["../../../../../src/services/workout/util/VolumePlanning/WorkoutVolumePlanningService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAGnC,OAAO,KAAK,2BAA2B,MAAM,gDAAgD,CAAC;AAG9F;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,OAAO,OAAO,4BAA4B;IAC/C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAK;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qCAAqC,CAAM;IAEnE;;OAEG;IACH,MAAM,CAAC,6BAA6B,CAClC,OAAO,EAAE,2BAA2B,EACpC,eAAe,EAAE,MAAM,EACvB,kBAAkB,EAAE,OAAO,GAC1B;QAAE,oBAAoB,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAAC,mBAAmB,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;KAAE;IA4B9E;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,6CAA6C;IAwO5D;;;;;;;;;OASG;IACH,OAAO,CAAC,MAAM,CAAC,yBAAyB;CA6BzC"}