fitness-progression-calculator 1.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.
- package/dist/calculator.d.ts +32 -0
- package/dist/calculator.js +277 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +10 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.js +2 -0
- package/package.json +21 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
interface ExerciseData {
|
2
|
+
sets: number;
|
3
|
+
reps: number;
|
4
|
+
weight: number;
|
5
|
+
rating: number;
|
6
|
+
equipment_type: "DUMBBELL" | "BARBELL" | "CABLE" | "MACHINE" | "BODYWEIGHT";
|
7
|
+
is_compound: boolean;
|
8
|
+
exercise_name: string;
|
9
|
+
}
|
10
|
+
interface ProgressionResult {
|
11
|
+
newWeight: number;
|
12
|
+
newReps: number;
|
13
|
+
deload?: boolean;
|
14
|
+
}
|
15
|
+
interface UserEquipmentSettings {
|
16
|
+
barbellIncrement: number;
|
17
|
+
dumbbellIncrement: number;
|
18
|
+
cableIncrement: number;
|
19
|
+
machineIncrement: number;
|
20
|
+
experienceLevel?: "BEGINNER" | "INTERMEDIATE" | "ADVANCED";
|
21
|
+
}
|
22
|
+
/**
|
23
|
+
* Main function to calculate progression based on exercise data and program type
|
24
|
+
* Now accepts optional userSettings for equipment-specific increments
|
25
|
+
*/
|
26
|
+
export declare function calculateProgression(data: ExerciseData, programType: "STRENGTH" | "HYPERTROPHY", userSettings?: UserEquipmentSettings): ProgressionResult;
|
27
|
+
/**
|
28
|
+
* Helper function to round weight to closest available increment
|
29
|
+
* Useful for client-side adjustments
|
30
|
+
*/
|
31
|
+
export declare function roundToClosestIncrement(weight: number, equipmentType: ExerciseData["equipment_type"], userSettings?: UserEquipmentSettings): number;
|
32
|
+
export type { ExerciseData, ProgressionResult, UserEquipmentSettings };
|
@@ -0,0 +1,277 @@
|
|
1
|
+
"use strict";
|
2
|
+
// services/progressionCalculator.ts
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
4
|
+
exports.calculateProgression = calculateProgression;
|
5
|
+
exports.roundToClosestIncrement = roundToClosestIncrement;
|
6
|
+
const MAX_REPS = 20;
|
7
|
+
const BODYWEIGHT_MAX_REPS = 15; // Before considering adding weight
|
8
|
+
const SPECIAL_BODYWEIGHT_EXERCISES = [
|
9
|
+
"PULL UP",
|
10
|
+
"CHIN UP",
|
11
|
+
"DIP",
|
12
|
+
"PUSH UP",
|
13
|
+
"PUSH UP DEFICIT",
|
14
|
+
];
|
15
|
+
const DEFAULT_EQUIPMENT_SETTINGS = {
|
16
|
+
barbellIncrement: 2.5,
|
17
|
+
dumbbellIncrement: 2.0,
|
18
|
+
cableIncrement: 2.5,
|
19
|
+
machineIncrement: 5.0,
|
20
|
+
experienceLevel: "BEGINNER",
|
21
|
+
};
|
22
|
+
function isSpecialBodyweightExercise(exerciseName) {
|
23
|
+
return SPECIAL_BODYWEIGHT_EXERCISES.includes(exerciseName.toUpperCase());
|
24
|
+
}
|
25
|
+
/**
|
26
|
+
* Gets user's preferred increment for the given equipment type
|
27
|
+
*/
|
28
|
+
function getPreferredIncrement(equipmentType, userSettings = DEFAULT_EQUIPMENT_SETTINGS) {
|
29
|
+
switch (equipmentType) {
|
30
|
+
case "BARBELL":
|
31
|
+
return (userSettings.barbellIncrement ||
|
32
|
+
DEFAULT_EQUIPMENT_SETTINGS.barbellIncrement);
|
33
|
+
case "DUMBBELL":
|
34
|
+
return (userSettings.dumbbellIncrement ||
|
35
|
+
DEFAULT_EQUIPMENT_SETTINGS.dumbbellIncrement);
|
36
|
+
case "CABLE":
|
37
|
+
return (userSettings.cableIncrement || DEFAULT_EQUIPMENT_SETTINGS.cableIncrement);
|
38
|
+
case "MACHINE":
|
39
|
+
return (userSettings.machineIncrement ||
|
40
|
+
DEFAULT_EQUIPMENT_SETTINGS.machineIncrement);
|
41
|
+
case "BODYWEIGHT":
|
42
|
+
return (userSettings.dumbbellIncrement ||
|
43
|
+
DEFAULT_EQUIPMENT_SETTINGS.dumbbellIncrement); // Use dumbbell increment for weighted bodyweight
|
44
|
+
default:
|
45
|
+
return 2.5; // Fallback
|
46
|
+
}
|
47
|
+
}
|
48
|
+
/**
|
49
|
+
* Gets equipment-specific increments from user settings
|
50
|
+
*/
|
51
|
+
function getEquipmentIncrements(equipmentType, userSettings = DEFAULT_EQUIPMENT_SETTINGS) {
|
52
|
+
// Define standard increments available for each equipment type
|
53
|
+
const standardIncrements = {
|
54
|
+
BARBELL: [1.25, 2.5, 5.0, 10.0],
|
55
|
+
DUMBBELL: [1.0, 2.0, 2.5, 5.0],
|
56
|
+
CABLE: [2.0, 2.5, 5.0, 10.0],
|
57
|
+
MACHINE: [2.5, 5.0, 10.0],
|
58
|
+
BODYWEIGHT: [1.0, 2.5, 5.0], // For weighted bodyweight exercises
|
59
|
+
};
|
60
|
+
// Get user's preferred increment for this equipment type
|
61
|
+
const preferredIncrement = getPreferredIncrement(equipmentType, userSettings);
|
62
|
+
// Return equipment's standard increments with preferred increment first
|
63
|
+
return [
|
64
|
+
preferredIncrement,
|
65
|
+
...standardIncrements[equipmentType].filter((inc) => inc !== preferredIncrement),
|
66
|
+
];
|
67
|
+
}
|
68
|
+
/**
|
69
|
+
* Finds the closest available increment to the desired raw increment
|
70
|
+
*/
|
71
|
+
function findClosestIncrement(rawIncrement, availableIncrements) {
|
72
|
+
// If rawIncrement is smaller than smallest available increment, return smallest
|
73
|
+
if (rawIncrement <= availableIncrements[0]) {
|
74
|
+
return availableIncrements[0];
|
75
|
+
}
|
76
|
+
// Find closest increment by minimizing absolute difference
|
77
|
+
return availableIncrements.reduce((prev, curr) => Math.abs(curr - rawIncrement) < Math.abs(prev - rawIncrement) ? curr : prev);
|
78
|
+
}
|
79
|
+
/**
|
80
|
+
* Determines the appropriate standard increment based on exercise type, weight and experience level
|
81
|
+
*/
|
82
|
+
function getStandardIncrement(currentWeight, equipmentType, isCompound, userSettings = DEFAULT_EQUIPMENT_SETTINGS) {
|
83
|
+
// Get available increments for this equipment type
|
84
|
+
const availableIncrements = getEquipmentIncrements(equipmentType, userSettings);
|
85
|
+
const sortedIncrements = [...availableIncrements].sort((a, b) => a - b);
|
86
|
+
const smallestIncrement = sortedIncrements[0] || 1.25;
|
87
|
+
// Base increment size on exercise type and current weight
|
88
|
+
let rawIncrement;
|
89
|
+
if (isCompound) {
|
90
|
+
// Compound movements (bench, squat, deadlift):
|
91
|
+
if (currentWeight < 40) {
|
92
|
+
rawIncrement = 5.0; // Under 40kg: 5kg increments
|
93
|
+
}
|
94
|
+
else if (currentWeight < 100) {
|
95
|
+
rawIncrement = 2.5; // 40-100kg: 2.5kg increments
|
96
|
+
}
|
97
|
+
else {
|
98
|
+
rawIncrement = 1.25; // Over 100kg: 1.25kg increments
|
99
|
+
}
|
100
|
+
}
|
101
|
+
else {
|
102
|
+
// Isolation movements (curls, lateral raises):
|
103
|
+
if (currentWeight < 15) {
|
104
|
+
rawIncrement = 2.5; // Under 15kg: 2.5kg increments
|
105
|
+
}
|
106
|
+
else {
|
107
|
+
rawIncrement = 1.25; // Over 15kg: 1.25kg increments
|
108
|
+
}
|
109
|
+
}
|
110
|
+
// Modify based on experience level
|
111
|
+
const experienceLevel = (userSettings === null || userSettings === void 0 ? void 0 : userSettings.experienceLevel) || "BEGINNER";
|
112
|
+
if (experienceLevel === "INTERMEDIATE" && currentWeight > 60) {
|
113
|
+
// Intermediate lifters progress slower at higher weights
|
114
|
+
rawIncrement = Math.max(smallestIncrement, rawIncrement * 0.5);
|
115
|
+
}
|
116
|
+
else if (experienceLevel === "ADVANCED") {
|
117
|
+
// Advanced lifters progress slower overall
|
118
|
+
rawIncrement = Math.max(smallestIncrement, rawIncrement * 0.5);
|
119
|
+
}
|
120
|
+
// Find the closest available increment
|
121
|
+
return findClosestIncrement(rawIncrement, availableIncrements);
|
122
|
+
}
|
123
|
+
/**
|
124
|
+
* Gets weight increment based on difficulty rating, equipment, weight and experience level
|
125
|
+
*/
|
126
|
+
function getWeightIncrement(currentWeight, equipment, isCompound, rating, userSettings) {
|
127
|
+
// Get the standard increment for this exercise/weight
|
128
|
+
const standardIncrement = getStandardIncrement(currentWeight, equipment, isCompound, userSettings);
|
129
|
+
// Modify based on rating
|
130
|
+
switch (rating) {
|
131
|
+
case 1: // Very Easy - double the increment
|
132
|
+
return standardIncrement * 2;
|
133
|
+
case 2: // Easy - standard increment
|
134
|
+
case 3: // Moderate - standard increment
|
135
|
+
return standardIncrement;
|
136
|
+
case 4: // Hard - no increase
|
137
|
+
return 0;
|
138
|
+
case 5: // Too Hard - decrease
|
139
|
+
return -standardIncrement;
|
140
|
+
default:
|
141
|
+
return standardIncrement;
|
142
|
+
}
|
143
|
+
}
|
144
|
+
function calculateBodyweightProgression(data) {
|
145
|
+
switch (data.rating) {
|
146
|
+
case 1: // Very easy
|
147
|
+
return {
|
148
|
+
newWeight: 0,
|
149
|
+
newReps: Math.min(data.reps + 2, MAX_REPS),
|
150
|
+
};
|
151
|
+
case 2: // Easy
|
152
|
+
return {
|
153
|
+
newWeight: 0,
|
154
|
+
newReps: Math.min(data.reps + 1, MAX_REPS),
|
155
|
+
};
|
156
|
+
case 3: // Moderate
|
157
|
+
return {
|
158
|
+
newWeight: 0,
|
159
|
+
newReps: Math.min(data.reps + 1, MAX_REPS),
|
160
|
+
};
|
161
|
+
case 4: // Hard
|
162
|
+
return {
|
163
|
+
newWeight: 0,
|
164
|
+
newReps: data.reps,
|
165
|
+
};
|
166
|
+
case 5: // Too hard
|
167
|
+
return {
|
168
|
+
newWeight: 0,
|
169
|
+
newReps: Math.max(1, data.reps - 2),
|
170
|
+
};
|
171
|
+
default:
|
172
|
+
return {
|
173
|
+
newWeight: 0,
|
174
|
+
newReps: data.reps,
|
175
|
+
};
|
176
|
+
}
|
177
|
+
}
|
178
|
+
function calculateStrengthProgression(data, userSettings) {
|
179
|
+
// Handle special bodyweight exercises differently
|
180
|
+
if (data.equipment_type === "BODYWEIGHT" &&
|
181
|
+
isSpecialBodyweightExercise(data.exercise_name)) {
|
182
|
+
return calculateBodyweightProgression(data);
|
183
|
+
}
|
184
|
+
const increment = getWeightIncrement(data.weight, data.equipment_type, data.is_compound, data.rating, userSettings);
|
185
|
+
// Apply the calculated increment
|
186
|
+
return {
|
187
|
+
newWeight: Math.max(0, data.weight + increment),
|
188
|
+
newReps: data.reps,
|
189
|
+
};
|
190
|
+
}
|
191
|
+
function calculateVolumeIncrease(data, type, userSettings) {
|
192
|
+
const increment = getWeightIncrement(data.weight, data.equipment_type, data.is_compound, Math.min(data.rating, 3), // Cap at 3 to always get a positive increment for comparison
|
193
|
+
userSettings);
|
194
|
+
const currentVolume = data.sets * data.reps * data.weight;
|
195
|
+
if (type === "weight") {
|
196
|
+
return data.sets * data.reps * (data.weight + increment) - currentVolume;
|
197
|
+
}
|
198
|
+
// Only calculate rep increase if we're not at max reps
|
199
|
+
if (data.reps >= MAX_REPS) {
|
200
|
+
return Number.POSITIVE_INFINITY; // Force weight increase by making rep increase unfavorable
|
201
|
+
}
|
202
|
+
return data.sets * (data.reps + 1) * data.weight - currentVolume;
|
203
|
+
}
|
204
|
+
function calculateHypertrophyProgression(data, userSettings) {
|
205
|
+
// Handle special bodyweight exercises differently
|
206
|
+
if (data.equipment_type === "BODYWEIGHT" &&
|
207
|
+
isSpecialBodyweightExercise(data.exercise_name)) {
|
208
|
+
return calculateBodyweightProgression(data);
|
209
|
+
}
|
210
|
+
const increment = getWeightIncrement(data.weight, data.equipment_type, data.is_compound, data.rating, userSettings);
|
211
|
+
// For compound movements (except special bodyweight exercises), prioritize weight increases
|
212
|
+
if (data.is_compound) {
|
213
|
+
return calculateStrengthProgression(data, userSettings);
|
214
|
+
}
|
215
|
+
// For isolation exercises, implement progressive overload based on rating
|
216
|
+
switch (data.rating) {
|
217
|
+
case 1: {
|
218
|
+
// Very easy
|
219
|
+
const newReps = data.reps >= MAX_REPS ? data.reps : data.reps + 1;
|
220
|
+
return {
|
221
|
+
newWeight: data.weight + increment,
|
222
|
+
newReps: newReps,
|
223
|
+
};
|
224
|
+
}
|
225
|
+
case 2: {
|
226
|
+
// Easy - increase either weight or reps (larger increase)
|
227
|
+
if (data.reps >= MAX_REPS) {
|
228
|
+
return { newWeight: data.weight + increment, newReps: data.reps };
|
229
|
+
}
|
230
|
+
const volumeIncreaseWeight = calculateVolumeIncrease(data, "weight", userSettings);
|
231
|
+
const volumeIncreaseReps = calculateVolumeIncrease(data, "reps", userSettings);
|
232
|
+
return volumeIncreaseWeight > volumeIncreaseReps
|
233
|
+
? { newWeight: data.weight + increment, newReps: data.reps }
|
234
|
+
: { newWeight: data.weight, newReps: data.reps + 1 };
|
235
|
+
}
|
236
|
+
case 3: {
|
237
|
+
// Moderate - choose smallest increase between weight and reps
|
238
|
+
if (data.reps >= MAX_REPS) {
|
239
|
+
return { newWeight: data.weight + increment, newReps: data.reps };
|
240
|
+
}
|
241
|
+
const weightIncrease = calculateVolumeIncrease(data, "weight", userSettings);
|
242
|
+
const repIncrease = calculateVolumeIncrease(data, "reps", userSettings);
|
243
|
+
return weightIncrease < repIncrease
|
244
|
+
? { newWeight: data.weight + increment, newReps: data.reps }
|
245
|
+
: { newWeight: data.weight, newReps: data.reps + 1 };
|
246
|
+
}
|
247
|
+
case 4: // Hard
|
248
|
+
return { newWeight: data.weight, newReps: data.reps };
|
249
|
+
case 5: // Too hard
|
250
|
+
return {
|
251
|
+
newWeight: Math.max(0, data.weight + increment),
|
252
|
+
newReps: data.reps,
|
253
|
+
};
|
254
|
+
default:
|
255
|
+
return { newWeight: data.weight, newReps: data.reps };
|
256
|
+
}
|
257
|
+
}
|
258
|
+
/**
|
259
|
+
* Main function to calculate progression based on exercise data and program type
|
260
|
+
* Now accepts optional userSettings for equipment-specific increments
|
261
|
+
*/
|
262
|
+
function calculateProgression(data, programType, userSettings) {
|
263
|
+
if (programType === "STRENGTH") {
|
264
|
+
return calculateStrengthProgression(data, userSettings);
|
265
|
+
}
|
266
|
+
return calculateHypertrophyProgression(data, userSettings);
|
267
|
+
}
|
268
|
+
/**
|
269
|
+
* Helper function to round weight to closest available increment
|
270
|
+
* Useful for client-side adjustments
|
271
|
+
*/
|
272
|
+
function roundToClosestIncrement(weight, equipmentType, userSettings) {
|
273
|
+
const availableIncrements = getEquipmentIncrements(equipmentType, userSettings);
|
274
|
+
const preferredIncrement = availableIncrements[0];
|
275
|
+
// Round to nearest multiple of preferred increment
|
276
|
+
return Math.round(weight / preferredIncrement) * preferredIncrement;
|
277
|
+
}
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
import type { ExerciseData, ProgressionResult, ProgramType, UserEquipmentSettings } from "./types";
|
2
|
+
import { roundToClosestIncrement } from "./calculator";
|
3
|
+
export declare function calculateProgression(data: ExerciseData, programType: ProgramType, userSettings?: UserEquipmentSettings): ProgressionResult;
|
4
|
+
export type { ExerciseData, ProgressionResult, ProgramType, UserEquipmentSettings, };
|
5
|
+
export { roundToClosestIncrement };
|
package/dist/index.js
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.roundToClosestIncrement = void 0;
|
4
|
+
exports.calculateProgression = calculateProgression;
|
5
|
+
const calculator_1 = require("./calculator");
|
6
|
+
Object.defineProperty(exports, "roundToClosestIncrement", { enumerable: true, get: function () { return calculator_1.roundToClosestIncrement; } });
|
7
|
+
// Re-export the main function
|
8
|
+
function calculateProgression(data, programType, userSettings) {
|
9
|
+
return (0, calculator_1.calculateProgression)(data, programType, userSettings);
|
10
|
+
}
|
package/dist/types.d.ts
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
export interface UserEquipmentSettings {
|
2
|
+
barbellIncrement: number;
|
3
|
+
dumbbellIncrement: number;
|
4
|
+
cableIncrement: number;
|
5
|
+
machineIncrement: number;
|
6
|
+
experienceLevel?: "BEGINNER" | "INTERMEDIATE" | "ADVANCED";
|
7
|
+
}
|
8
|
+
export interface ExerciseData {
|
9
|
+
sets: number;
|
10
|
+
reps: number;
|
11
|
+
weight: number;
|
12
|
+
rating: number;
|
13
|
+
equipment_type: "DUMBBELL" | "BARBELL" | "CABLE" | "MACHINE" | "BODYWEIGHT";
|
14
|
+
is_compound: boolean;
|
15
|
+
exercise_name: string;
|
16
|
+
}
|
17
|
+
export interface ProgressionResult {
|
18
|
+
newWeight: number;
|
19
|
+
newReps: number;
|
20
|
+
deload?: boolean;
|
21
|
+
}
|
22
|
+
export type ProgramType = "STRENGTH" | "HYPERTROPHY";
|
package/dist/types.js
ADDED
package/package.json
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"name": "fitness-progression-calculator",
|
3
|
+
"version": "1.1.0",
|
4
|
+
"description": "Workout progression calculator for fitness applications",
|
5
|
+
"main": "dist/index.js",
|
6
|
+
"types": "dist/index.d.ts",
|
7
|
+
"module": "dist/index.js",
|
8
|
+
"react-native": "dist/index.js",
|
9
|
+
"scripts": {
|
10
|
+
"build": "tsc",
|
11
|
+
"prepare": "npm run build"
|
12
|
+
},
|
13
|
+
"keywords": ["fitness", "workout", "progression"],
|
14
|
+
"author": "Your Name",
|
15
|
+
"license": "MIT",
|
16
|
+
"files": ["dist/**/*"],
|
17
|
+
"devDependencies": {
|
18
|
+
"@types/node": "^18.0.0",
|
19
|
+
"typescript": "^4.9.5"
|
20
|
+
}
|
21
|
+
}
|