@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,159 @@
1
+ import type { UUID } from 'crypto';
2
+ import type { WorkoutEquipmentType } from '../../../documents/workout/WorkoutEquipmentType.js';
3
+ import type { WorkoutExercise } from '../../../documents/workout/WorkoutExercise.js';
4
+ import type {
5
+ CalibrationExercisePair,
6
+ WorkoutExerciseCalibration
7
+ } from '../../../documents/workout/WorkoutExerciseCalibration.js';
8
+ import type { WorkoutMesocycle } from '../../../documents/workout/WorkoutMesocycle.js';
9
+ import type { WorkoutMicrocycle } from '../../../documents/workout/WorkoutMicrocycle.js';
10
+ import type { WorkoutSession } from '../../../documents/workout/WorkoutSession.js';
11
+ import type { WorkoutSessionExercise } from '../../../documents/workout/WorkoutSessionExercise.js';
12
+ import type { WorkoutSet } from '../../../documents/workout/WorkoutSet.js';
13
+
14
+ /**
15
+ * Central shared context for generating or updating a {@link WorkoutMesocycle}.
16
+ *
17
+ * This keeps the generation pipeline (Mesocycle -> Microcycle -> Session -> Set) from needing to
18
+ * thread the same inputs (mesocycle config + document lookups + optional historical docs) through
19
+ * every method.
20
+ */
21
+ export default class WorkoutMesocyclePlanContext {
22
+ /**
23
+ * A constant for now that defines what the target RIR is for the first microcycle of every mesocycle,
24
+ * regardless of anything else.
25
+ */
26
+ public readonly FIRST_MICROCYCLE_RIR = 4;
27
+
28
+ public readonly calibrationMap: Map<UUID, WorkoutExerciseCalibration>;
29
+ public readonly exerciseMap: Map<UUID, WorkoutExercise>;
30
+ public readonly equipmentMap: Map<UUID, WorkoutEquipmentType>;
31
+ public readonly sessionMap: Map<UUID, WorkoutSession>;
32
+ public readonly sessionExerciseMap: Map<UUID, WorkoutSessionExercise>;
33
+
34
+ public readonly microcyclesToCreate: WorkoutMicrocycle[] = [];
35
+ /**
36
+ * All microcycles for this mesocycle in chronological order.
37
+ *
38
+ * This includes any existing microcycles (filtered to the target mesocycle) and
39
+ * all newly generated microcycles.
40
+ */
41
+ public readonly microcyclesInOrder: WorkoutMicrocycle[] = [];
42
+ public readonly sessionsToCreate: WorkoutSession[] = [];
43
+ public readonly sessionExercisesToCreate: WorkoutSessionExercise[] = [];
44
+ public readonly setsToCreate: WorkoutSet[] = [];
45
+
46
+ /**
47
+ * The array of sessions corresponding to the array of calibration exercise pairs, in order,
48
+ * for that session.
49
+ */
50
+ public plannedSessionExercisePairs: CalibrationExercisePair[][] | undefined;
51
+ /**
52
+ * Stores the mesocycle's muscle-group-wide exercise ordering.
53
+ *
54
+ * This is used by session generation to distribute set progression evenly across
55
+ * exercises that share a primary muscle group, regardless of which session they
56
+ * ended up in.
57
+ */
58
+ public muscleGroupToExercisePairsMap: Map<UUID, CalibrationExercisePair[]> | undefined;
59
+ public exerciseIdToSessionIndex: Map<UUID, number> | undefined;
60
+
61
+ /**
62
+ * Creates a new workout mesocycle planning context.
63
+ */
64
+ constructor(
65
+ public mesocycle: WorkoutMesocycle,
66
+ calibrations: WorkoutExerciseCalibration[],
67
+ exercises: WorkoutExercise[],
68
+ equipmentTypes: WorkoutEquipmentType[],
69
+ public existingMicrocycles: WorkoutMicrocycle[] = [],
70
+ public existingSessions: WorkoutSession[] = [],
71
+ public existingSessionExercises: WorkoutSessionExercise[] = [],
72
+ public existingSets: WorkoutSet[] = []
73
+ ) {
74
+ this.calibrationMap = new Map(calibrations.map((c) => [c._id, c]));
75
+ this.exerciseMap = new Map(exercises.map((e) => [e._id, e]));
76
+ this.equipmentMap = new Map(equipmentTypes.map((et) => [et._id, et]));
77
+ this.sessionMap = new Map(existingSessions.map((s) => [s._id, s]));
78
+ this.sessionExerciseMap = new Map(existingSessionExercises.map((s) => [s._id, s]));
79
+
80
+ const existingMicrocyclesForMesocycle = existingMicrocycles
81
+ .filter((m) => m.workoutMesocycleId === mesocycle._id)
82
+ .sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
83
+ this.microcyclesInOrder.push(...existingMicrocyclesForMesocycle);
84
+ }
85
+
86
+ /**
87
+ * Adds a microcycle to the list of microcycles to create and the overall chronological list.
88
+ */
89
+ public addMicrocycle(toCreate: WorkoutMicrocycle): void {
90
+ this.microcyclesToCreate.push(toCreate);
91
+ this.microcyclesInOrder.push(toCreate);
92
+ }
93
+
94
+ /**
95
+ * Adds a session to the context and updates internal maps.
96
+ */
97
+ public addSession(session: WorkoutSession): void {
98
+ this.sessionsToCreate.push(session);
99
+ this.sessionMap.set(session._id, session);
100
+ }
101
+
102
+ /**
103
+ * Adds a session exercise to the context and updates internal maps.
104
+ */
105
+ public addSessionExercise(sessionExercise: WorkoutSessionExercise): void {
106
+ this.sessionExercisesToCreate.push(sessionExercise);
107
+ this.sessionExerciseMap.set(sessionExercise._id, sessionExercise);
108
+ }
109
+
110
+ /**
111
+ * Stores the planned session -> exercises structure for the mesocycle and derives
112
+ * the muscle-group-wide ordering used for set progression.
113
+ */
114
+ public setPlannedSessionExercisePairs(
115
+ plannedSessionExercisePairs: CalibrationExercisePair[][]
116
+ ): void {
117
+ this.plannedSessionExercisePairs = plannedSessionExercisePairs;
118
+ this.buildSessionPairsMaps(plannedSessionExercisePairs);
119
+ }
120
+
121
+ /**
122
+ * Builds and stores a map of primary muscle group -> ordered exercise pairs for the mesocycle plan.
123
+ *
124
+ * Also builds a map of exercise ID -> session index for use during session generation.
125
+ *
126
+ * The ordering is determined by flattening the planned sessions in order, then the exercises within
127
+ * each session in order. This allows set progression to be distributed evenly across a muscle group
128
+ * for the entire microcycle, even when exercises are split across sessions.
129
+ */
130
+ private buildSessionPairsMaps(sessionsToExercisePairs: CalibrationExercisePair[][]): void {
131
+ const muscleGroupToPairsMap = new Map<UUID, CalibrationExercisePair[]>();
132
+ const exerciseIdToSessionIndex = new Map<UUID, number>();
133
+
134
+ for (let sessionIndex = 0; sessionIndex < sessionsToExercisePairs.length; sessionIndex++) {
135
+ const sessionExercisePairs = sessionsToExercisePairs[sessionIndex];
136
+ for (const exercisePair of sessionExercisePairs) {
137
+ // Set the session index for this exercise as well
138
+ exerciseIdToSessionIndex.set(exercisePair.exercise._id, sessionIndex);
139
+
140
+ const primaryMuscleGroupId = exercisePair.exercise.primaryMuscleGroups[0];
141
+ if (!primaryMuscleGroupId) {
142
+ throw new Error(
143
+ `Exercise ${exercisePair.exercise._id}, ${exercisePair.exercise.exerciseName} has no primary muscle group`
144
+ );
145
+ }
146
+
147
+ const existing = muscleGroupToPairsMap.get(primaryMuscleGroupId);
148
+ if (existing) {
149
+ existing.push(exercisePair);
150
+ } else {
151
+ muscleGroupToPairsMap.set(primaryMuscleGroupId, [exercisePair]);
152
+ }
153
+ }
154
+ }
155
+
156
+ this.muscleGroupToExercisePairsMap = muscleGroupToPairsMap;
157
+ this.exerciseIdToSessionIndex = exerciseIdToSessionIndex;
158
+ }
159
+ }
@@ -0,0 +1,52 @@
1
+ import type { WorkoutEquipmentType } from '../../../documents/workout/WorkoutEquipmentType.js';
2
+ import type { WorkoutExercise } from '../../../documents/workout/WorkoutExercise.js';
3
+ import type { WorkoutExerciseCalibration } from '../../../documents/workout/WorkoutExerciseCalibration.js';
4
+ import { type WorkoutMesocycle } from '../../../documents/workout/WorkoutMesocycle.js';
5
+ import type { WorkoutMicrocycle } from '../../../documents/workout/WorkoutMicrocycle.js';
6
+ import type { WorkoutSession } from '../../../documents/workout/WorkoutSession.js';
7
+ import type { WorkoutSessionExercise } from '../../../documents/workout/WorkoutSessionExercise.js';
8
+ import type { WorkoutSet } from '../../../documents/workout/WorkoutSet.js';
9
+ import type { DocumentOperations } from '../../DocumentService.js';
10
+ /**
11
+ * A service for handling operations related to {@link WorkoutMesocycle}s.
12
+ */
13
+ export default class WorkoutMesocycleService {
14
+ /**
15
+ * Generates or updates the workout plan for a mesocycle.
16
+ *
17
+ * This method supports incremental generation by only creating new microcycles that don't
18
+ * yet exist. It expects that the mesocycle's core parameters (planned session count,
19
+ * microcycle count, microcycle length) and exercise ordering have not changed since
20
+ * initial creation. If these parameters have changed, it is the responsibility of a
21
+ * higher-level service to convert the mesocycle to free-form mode before calling this method.
22
+ *
23
+ * The method will clean up any incomplete microcycles (where the last session is not
24
+ * complete) and regenerate from that point, unless the microcycle has already started
25
+ * (first session complete), in which case it will throw an error.
26
+ *
27
+ * @param mesocycle The mesocycle configuration.
28
+ * @param calibrations The calibration documents referenced by the mesocycle.
29
+ * @param exercises The exercise definitions for the calibrations.
30
+ * @param equipmentTypes The equipment types for weight increment calculations.
31
+ * @param existingMicrocycles Existing microcycle documents for this mesocycle.
32
+ * @param existingSessions Existing session documents.
33
+ * @param existingSessionExercises Existing session exercise documents.
34
+ * @param existingSets Existing set documents.
35
+ */
36
+ static generateOrUpdateMesocycle(mesocycle: WorkoutMesocycle, calibrations: WorkoutExerciseCalibration[], exercises: WorkoutExercise[], equipmentTypes: WorkoutEquipmentType[], existingMicrocycles?: WorkoutMicrocycle[], existingSessions?: WorkoutSession[], existingSessionExercises?: WorkoutSessionExercise[], existingSets?: WorkoutSet[]): {
37
+ mesocycleUpdate?: Partial<WorkoutMesocycle>;
38
+ microcycles?: DocumentOperations<WorkoutMicrocycle>;
39
+ sessions?: DocumentOperations<WorkoutSession>;
40
+ sessionExercises?: DocumentOperations<WorkoutSessionExercise>;
41
+ sets?: DocumentOperations<WorkoutSet>;
42
+ };
43
+ /**
44
+ * Cleans up incomplete microcycles and their associated documents.
45
+ *
46
+ * Finds the first microcycle where the last session is not complete, validates that it
47
+ * hasn't started (first session incomplete), and returns IDs of all documents that should
48
+ * be deleted (microcycles from that point forward and all their associated data).
49
+ */
50
+ private static cleanUpIncompleteMicrocycles;
51
+ }
52
+ //# sourceMappingURL=WorkoutMesocycleService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkoutMesocycleService.d.ts","sourceRoot":"","sources":["../../../../src/services/workout/Mesocycle/WorkoutMesocycleService.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oDAAoD,CAAC;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+CAA+C,CAAC;AACrF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0DAA0D,CAAC;AAC3G,OAAO,EAAa,KAAK,gBAAgB,EAAE,MAAM,gDAAgD,CAAC;AAClG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iDAAiD,CAAC;AAEzF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8CAA8C,CAAC;AACnF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sDAAsD,CAAC;AACnG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,uBAAuB;IAC1C;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,MAAM,CAAC,yBAAyB,CAC9B,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EAAE,0BAA0B,EAAE,EAC1C,SAAS,EAAE,eAAe,EAAE,EAC5B,cAAc,EAAE,oBAAoB,EAAE,EACtC,mBAAmB,GAAE,iBAAiB,EAAO,EAC7C,gBAAgB,GAAE,cAAc,EAAO,EACvC,wBAAwB,GAAE,sBAAsB,EAAO,EACvD,YAAY,GAAE,UAAU,EAAO,GAC9B;QACD,eAAe,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC5C,WAAW,CAAC,EAAE,kBAAkB,CAAC,iBAAiB,CAAC,CAAC;QACpD,QAAQ,CAAC,EAAE,kBAAkB,CAAC,cAAc,CAAC,CAAC;QAC9C,gBAAgB,CAAC,EAAE,kBAAkB,CAAC,sBAAsB,CAAC,CAAC;QAC9D,IAAI,CAAC,EAAE,kBAAkB,CAAC,UAAU,CAAC,CAAC;KACvC;IAyHD;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,4BAA4B;CAyF5C"}
@@ -0,0 +1,180 @@
1
+ import { DateService } from '@aneuhold/core-ts-lib';
2
+ import { CycleType } from '../../../documents/workout/WorkoutMesocycle.js';
3
+ import { WorkoutMicrocycleSchema } from '../../../documents/workout/WorkoutMicrocycle.js';
4
+ import WorkoutMicrocycleService from '../Microcycle/WorkoutMicrocycleService.js';
5
+ import WorkoutMesocyclePlanContext from './WorkoutMesocyclePlanContext.js';
6
+ /**
7
+ * A service for handling operations related to {@link WorkoutMesocycle}s.
8
+ */
9
+ export default class WorkoutMesocycleService {
10
+ /**
11
+ * Generates or updates the workout plan for a mesocycle.
12
+ *
13
+ * This method supports incremental generation by only creating new microcycles that don't
14
+ * yet exist. It expects that the mesocycle's core parameters (planned session count,
15
+ * microcycle count, microcycle length) and exercise ordering have not changed since
16
+ * initial creation. If these parameters have changed, it is the responsibility of a
17
+ * higher-level service to convert the mesocycle to free-form mode before calling this method.
18
+ *
19
+ * The method will clean up any incomplete microcycles (where the last session is not
20
+ * complete) and regenerate from that point, unless the microcycle has already started
21
+ * (first session complete), in which case it will throw an error.
22
+ *
23
+ * @param mesocycle The mesocycle configuration.
24
+ * @param calibrations The calibration documents referenced by the mesocycle.
25
+ * @param exercises The exercise definitions for the calibrations.
26
+ * @param equipmentTypes The equipment types for weight increment calculations.
27
+ * @param existingMicrocycles Existing microcycle documents for this mesocycle.
28
+ * @param existingSessions Existing session documents.
29
+ * @param existingSessionExercises Existing session exercise documents.
30
+ * @param existingSets Existing set documents.
31
+ */
32
+ static generateOrUpdateMesocycle(mesocycle, calibrations, exercises, equipmentTypes, existingMicrocycles = [], existingSessions = [], existingSessionExercises = [], existingSets = []) {
33
+ // Free-form mesocycles are intentionally not auto-planned. The user can still log workouts,
34
+ // but we avoid generating microcycles/sessions/sets because recommendations wouldn't be able
35
+ // to be done / make any sense.
36
+ if (mesocycle.cycleType === CycleType.FreeForm) {
37
+ return {};
38
+ }
39
+ // Clean up incomplete microcycles before creating context
40
+ const cleanupResult = this.cleanUpIncompleteMicrocycles(mesocycle, existingMicrocycles, existingSessions, existingSessionExercises, existingSets);
41
+ // Filter out documents that will be deleted
42
+ const cleanMicrocycles = existingMicrocycles.filter((m) => !cleanupResult.microcyclesToDelete.includes(m._id));
43
+ const cleanSessions = existingSessions.filter((s) => !cleanupResult.sessionsToDelete.includes(s._id));
44
+ const cleanSessionExercises = existingSessionExercises.filter((se) => !cleanupResult.sessionExercisesToDelete.includes(se._id));
45
+ const cleanSets = existingSets.filter((s) => !cleanupResult.setsToDelete.includes(s._id));
46
+ // Create planning context with clean data
47
+ const context = new WorkoutMesocyclePlanContext(mesocycle, calibrations, exercises, equipmentTypes, cleanMicrocycles, cleanSessions, cleanSessionExercises, cleanSets);
48
+ // Distribute exercises across sessions once for the entire mesocycle plan.
49
+ // This session layout is expected to be stable across microcycles.
50
+ context.setPlannedSessionExercisePairs(WorkoutMicrocycleService.distributeExercisesAcrossSessions(mesocycle.plannedSessionCountPerMicrocycle, context.calibrationMap, context.exerciseMap));
51
+ // Determine number of microcycles (default to 6 if not specified: 5 accumulation + 1 deload)
52
+ const totalMicrocycles = mesocycle.plannedMicrocycleCount ?? 6;
53
+ const deloadMicrocycleIndex = totalMicrocycles - 1;
54
+ // Determine starting point for generation
55
+ const startMicrocycleIndex = context.microcyclesInOrder.length;
56
+ let currentDate;
57
+ if (startMicrocycleIndex === 0) {
58
+ // No existing microcycles, start from current date
59
+ currentDate = new Date();
60
+ }
61
+ else {
62
+ // Continue from where the last existing microcycle ended
63
+ const lastExistingMicrocycle = context.microcyclesInOrder[startMicrocycleIndex - 1];
64
+ currentDate = new Date(lastExistingMicrocycle.endDate);
65
+ }
66
+ // Generate remaining microcycles
67
+ for (let microcycleIndex = startMicrocycleIndex; microcycleIndex < totalMicrocycles; microcycleIndex++) {
68
+ const isDeloadMicrocycle = microcycleIndex === deloadMicrocycleIndex;
69
+ // Calculate RIR for this microcycle (4 -> 3 -> 2 -> 1 -> 0, capped at microcycle 5)
70
+ const rirForMicrocycle = Math.min(microcycleIndex, 4);
71
+ const targetRir = 4 - rirForMicrocycle;
72
+ // Create microcycle
73
+ const microcycle = WorkoutMicrocycleSchema.parse({
74
+ userId: mesocycle.userId,
75
+ workoutMesocycleId: mesocycle._id,
76
+ startDate: new Date(currentDate),
77
+ endDate: DateService.addDays(currentDate, mesocycle.plannedMicrocycleLengthInDays)
78
+ });
79
+ context.addMicrocycle(microcycle);
80
+ WorkoutMicrocycleService.generateSessionsForMicrocycle({
81
+ context,
82
+ microcycleIndex,
83
+ targetRir,
84
+ isDeloadMicrocycle
85
+ });
86
+ // Move to next microcycle
87
+ currentDate = new Date(microcycle.endDate);
88
+ }
89
+ return {
90
+ mesocycleUpdate: undefined,
91
+ microcycles: {
92
+ create: context.microcyclesToCreate,
93
+ update: [],
94
+ delete: cleanupResult.microcyclesToDelete
95
+ },
96
+ sessions: {
97
+ create: context.sessionsToCreate,
98
+ update: [],
99
+ delete: cleanupResult.sessionsToDelete
100
+ },
101
+ sessionExercises: {
102
+ create: context.sessionExercisesToCreate,
103
+ update: [],
104
+ delete: cleanupResult.sessionExercisesToDelete
105
+ },
106
+ sets: { create: context.setsToCreate, update: [], delete: cleanupResult.setsToDelete }
107
+ };
108
+ }
109
+ /**
110
+ * Cleans up incomplete microcycles and their associated documents.
111
+ *
112
+ * Finds the first microcycle where the last session is not complete, validates that it
113
+ * hasn't started (first session incomplete), and returns IDs of all documents that should
114
+ * be deleted (microcycles from that point forward and all their associated data).
115
+ */
116
+ static cleanUpIncompleteMicrocycles(mesocycle, existingMicrocycles, existingSessions, existingSessionExercises, existingSets) {
117
+ const microcyclesToDelete = [];
118
+ const sessionsToDelete = [];
119
+ const sessionExercisesToDelete = [];
120
+ const setsToDelete = [];
121
+ // Sort microcycles for this mesocycle
122
+ const microcyclesForMesocycle = existingMicrocycles.sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
123
+ // Find first incomplete microcycle
124
+ let firstIncompleteMicrocycleIndex = -1;
125
+ for (let i = 0; i < microcyclesForMesocycle.length; i++) {
126
+ const microcycle = microcyclesForMesocycle[i];
127
+ if (microcycle.sessionOrder.length === 0) {
128
+ // Microcycle has no sessions, it's incomplete
129
+ firstIncompleteMicrocycleIndex = i;
130
+ break;
131
+ }
132
+ // Check if last session is complete
133
+ const lastSessionId = microcycle.sessionOrder[microcycle.sessionOrder.length - 1];
134
+ const lastSession = existingSessions.find((s) => s._id === lastSessionId);
135
+ if (!lastSession?.complete) {
136
+ firstIncompleteMicrocycleIndex = i;
137
+ break;
138
+ }
139
+ }
140
+ // If all microcycles are complete, nothing to clean up
141
+ if (firstIncompleteMicrocycleIndex === -1) {
142
+ return { microcyclesToDelete, sessionsToDelete, sessionExercisesToDelete, setsToDelete };
143
+ }
144
+ const firstIncompleteMicrocycle = microcyclesForMesocycle[firstIncompleteMicrocycleIndex];
145
+ // Check if the incomplete microcycle has started (first session complete)
146
+ if (firstIncompleteMicrocycle.sessionOrder.length > 0) {
147
+ const firstSessionId = firstIncompleteMicrocycle.sessionOrder[0];
148
+ const firstSession = existingSessions.find((s) => s._id === firstSessionId);
149
+ if (firstSession?.complete) {
150
+ throw new Error(`Cannot generate new microcycles for mesocycle ${mesocycle._id}: ` +
151
+ `Microcycle at index ${firstIncompleteMicrocycleIndex} has started but is not complete. ` +
152
+ `All sessions in the current microcycle must be completed before generating new microcycles.`);
153
+ }
154
+ }
155
+ // Collect IDs of incomplete microcycles (from firstIncompleteMicrocycleIndex onward)
156
+ const incompleteMicrocycles = microcyclesForMesocycle.slice(firstIncompleteMicrocycleIndex);
157
+ microcyclesToDelete.push(...incompleteMicrocycles.map((m) => m._id));
158
+ // Collect all sessions, session exercises, and sets associated with incomplete microcycles
159
+ const incompleteMicrocycleIds = new Set(incompleteMicrocycles.map((m) => m._id));
160
+ for (const session of existingSessions) {
161
+ if (session.workoutMicrocycleId && incompleteMicrocycleIds.has(session.workoutMicrocycleId)) {
162
+ sessionsToDelete.push(session._id);
163
+ }
164
+ }
165
+ const sessionIdsToDelete = new Set(sessionsToDelete);
166
+ for (const sessionExercise of existingSessionExercises) {
167
+ if (sessionIdsToDelete.has(sessionExercise.workoutSessionId)) {
168
+ sessionExercisesToDelete.push(sessionExercise._id);
169
+ }
170
+ }
171
+ const sessionExerciseIdsToDelete = new Set(sessionExercisesToDelete);
172
+ for (const set of existingSets) {
173
+ if (sessionExerciseIdsToDelete.has(set.workoutSessionExerciseId)) {
174
+ setsToDelete.push(set._id);
175
+ }
176
+ }
177
+ return { microcyclesToDelete, sessionsToDelete, sessionExercisesToDelete, setsToDelete };
178
+ }
179
+ }
180
+ //# sourceMappingURL=WorkoutMesocycleService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkoutMesocycleService.js","sourceRoot":"","sources":["../../../../src/services/workout/Mesocycle/WorkoutMesocycleService.ts"],"names":[],"mappings":"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"}