hevy-shared 1.0.962 → 1.0.963

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 (88) hide show
  1. package/README.md +17 -2
  2. package/built/API/APIClient.d.ts +157 -0
  3. package/built/API/APIClient.js +381 -0
  4. package/built/API/index.d.ts +2 -0
  5. package/built/API/index.js +18 -0
  6. package/built/API/types.d.ts +38 -0
  7. package/built/API/types.js +18 -0
  8. package/built/adjustEventTokens.d.ts +16 -0
  9. package/built/adjustEventTokens.js +18 -0
  10. package/built/adminPermissions.d.ts +4 -0
  11. package/built/adminPermissions.js +22 -0
  12. package/built/async.d.ts +50 -0
  13. package/built/async.js +170 -0
  14. package/built/chat.d.ts +25 -23
  15. package/built/coachPlans.d.ts +2 -1
  16. package/built/coachPlans.js +2 -2
  17. package/built/cue.d.ts +12 -0
  18. package/built/cue.js +22 -0
  19. package/built/exerciseLocaleUtils.d.ts +17 -0
  20. package/built/exerciseLocaleUtils.js +62 -0
  21. package/built/filterExercises.d.ts +19 -3
  22. package/built/filterExercises.js +72 -60
  23. package/built/hevyTrainer.d.ts +250 -0
  24. package/built/hevyTrainer.js +676 -0
  25. package/built/index.d.ts +1140 -304
  26. package/built/index.js +269 -75
  27. package/built/muscleHeatmaps.d.ts +31 -0
  28. package/built/muscleHeatmaps.js +68 -0
  29. package/built/muscleSplits.d.ts +36 -0
  30. package/built/muscleSplits.js +100 -0
  31. package/built/normalizedWorkoutUtils.d.ts +88 -0
  32. package/built/normalizedWorkoutUtils.js +112 -0
  33. package/built/notifications.d.ts +215 -0
  34. package/built/notifications.js +9 -0
  35. package/built/routineUtils.d.ts +14 -0
  36. package/built/routineUtils.js +186 -0
  37. package/built/schemas.d.ts +14 -0
  38. package/built/schemas.js +9 -0
  39. package/built/setIndicatorUtils.d.ts +4 -3
  40. package/built/setIndicatorUtils.js +15 -1
  41. package/built/tests/async.test.d.ts +1 -0
  42. package/built/tests/async.test.js +49 -0
  43. package/built/tests/hevyTrainer.test.d.ts +1 -0
  44. package/built/tests/hevyTrainer.test.js +1199 -0
  45. package/built/tests/muscleSplit.test.d.ts +1 -0
  46. package/built/tests/muscleSplit.test.js +153 -0
  47. package/built/tests/routineUtils.test.d.ts +1 -0
  48. package/built/tests/routineUtils.test.js +745 -0
  49. package/built/tests/testUtils.d.ts +85 -0
  50. package/built/tests/testUtils.js +319 -0
  51. package/built/tests/utils.test.js +748 -0
  52. package/built/tests/workoutVolume.test.js +165 -49
  53. package/built/translations/index.d.ts +2 -0
  54. package/built/translations/index.js +18 -0
  55. package/built/translations/translationUtils.d.ts +2 -0
  56. package/built/translations/translationUtils.js +61 -0
  57. package/built/translations/types.d.ts +8 -0
  58. package/built/translations/types.js +20 -0
  59. package/built/typeUtils.d.ts +70 -0
  60. package/built/typeUtils.js +55 -0
  61. package/built/units.d.ts +14 -7
  62. package/built/units.js +24 -14
  63. package/built/utils.d.ts +192 -5
  64. package/built/utils.js +598 -85
  65. package/built/websocket.d.ts +14 -2
  66. package/built/workoutVolume.d.ts +24 -5
  67. package/built/workoutVolume.js +25 -34
  68. package/package.json +32 -10
  69. package/.eslintignore +0 -2
  70. package/.eslintrc +0 -21
  71. package/.github/workflows/ci.yml +0 -15
  72. package/.github/workflows/npm-publish.yml +0 -59
  73. package/.github/workflows/pr-auto-assign.yml +0 -15
  74. package/.prettierrc.js +0 -5
  75. package/jest.config.js +0 -4
  76. package/src/chat.ts +0 -130
  77. package/src/coachPlans.ts +0 -57
  78. package/src/constants.ts +0 -14
  79. package/src/filterExercises.ts +0 -222
  80. package/src/index.ts +0 -1576
  81. package/src/setIndicatorUtils.ts +0 -137
  82. package/src/tests/utils.test.ts +0 -156
  83. package/src/tests/workoutVolume.test.ts +0 -93
  84. package/src/units.ts +0 -41
  85. package/src/utils.ts +0 -516
  86. package/src/websocket.ts +0 -36
  87. package/src/workoutVolume.ts +0 -175
  88. package/tsconfig.json +0 -70
package/src/utils.ts DELETED
@@ -1,516 +0,0 @@
1
- import { CustomExercise, ExerciseTemplate } from '.';
2
-
3
- /**
4
- * Doesn't matter what you throw in the function it'll
5
- * always return a number. Non number values will return
6
- * 0.
7
- */
8
- export const num = (value?: any): number => {
9
- if (typeof value !== 'number') return 0;
10
- return value ?? 0;
11
- };
12
-
13
- export const divide = (numerator: number, denominator: number): number => {
14
- if (denominator === 0) return 0;
15
-
16
- return numerator / denominator;
17
- };
18
-
19
- export const isValidUsername = (username: string): boolean => {
20
- if (username.length > 20) {
21
- return false;
22
- }
23
-
24
- const lowercase = username.toLowerCase();
25
-
26
- if (
27
- lowercase.length < 3 ||
28
- lowercase.startsWith('_') ||
29
- lowercase.endsWith('_')
30
- ) {
31
- return false;
32
- }
33
-
34
- const re = /^[a-z0-9_]+$/;
35
- return re.test(lowercase);
36
- };
37
-
38
- /**
39
- * 01:25
40
- * 02:25:36
41
- */
42
- export const secondsToClockFormat = (seconds: number): string => {
43
- const hours = Math.floor(seconds / 3600);
44
- const minutes = Math.floor((seconds - hours * 3600) / 60);
45
- const remainderSeconds = seconds - hours * 3600 - minutes * 60;
46
-
47
- const paddWithLeadingZeros = (value: string): string => {
48
- if (value.length === 0) {
49
- return '00';
50
- }
51
-
52
- if (value.length === 1) {
53
- return `0${value}`;
54
- }
55
-
56
- return value;
57
- };
58
-
59
- if (hours) {
60
- const hoursString = paddWithLeadingZeros(`${hours}`);
61
- const minString = paddWithLeadingZeros(`${minutes}`);
62
- const secString = paddWithLeadingZeros(`${remainderSeconds}`);
63
- return `${hoursString}:${minString}:${secString}`;
64
- }
65
-
66
- return `${paddWithLeadingZeros(`${minutes}`)}:${paddWithLeadingZeros(
67
- `${remainderSeconds}`,
68
- )}`;
69
- };
70
-
71
- /**
72
- * 10s
73
- * 1min 10s
74
- * 2h 4min 45s
75
- */
76
- export const secondsToWordFormat = (seconds: number): string => {
77
- const hours = Math.floor(seconds / 3600);
78
- const minutes = Math.floor((seconds - hours * 3600) / 60);
79
- const remainderSeconds = Math.floor(seconds % 60);
80
-
81
- if (hours) {
82
- return `${hours}h ${minutes}min ${remainderSeconds}s`;
83
- }
84
-
85
- if (minutes) {
86
- return `${minutes}min ${remainderSeconds}s`;
87
- }
88
-
89
- return `${seconds}s`;
90
- };
91
-
92
- /**
93
- * 14min
94
- * 2h 4min
95
- */
96
- export const secondsToWordFormatMinutes = (seconds: number): string => {
97
- const hours = Math.floor(seconds / 3600);
98
- const minutes = Math.floor((seconds - hours * 3600) / 60);
99
-
100
- if (hours) {
101
- return `${hours}h ${minutes}min`;
102
- }
103
-
104
- return `${minutes}min`;
105
- };
106
-
107
- export const isValidEmail = (email: string): boolean => {
108
- const re =
109
- /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
110
- return re.test(String(email).toLowerCase());
111
- };
112
-
113
- export const isValidWebUrl = (url: string): boolean => {
114
- const re =
115
- /^(https?:\/\/)?(www\.)?[-a-z0-9.]{2,256}\.[a-z]{2,10}\b([-a-z0-9@:%_+.~#?&//=]*)$/;
116
- return re.test(String(url).toLowerCase());
117
- };
118
-
119
- /**
120
- * Check if a string is a valid phone number. A valid phone number is a string
121
- * that starts with a '+' followed by 9-15 digits with no spaces. This is the
122
- * format that we store in the database as well as what the Twilio API expects.
123
- *
124
- * @example
125
- * // valid:
126
- * +123456789
127
- * +123456789012345
128
- *
129
- * // not valid:
130
- * +1 23456789
131
- * 123456789
132
- * +1234567890123456
133
- */
134
- export const isValidPhoneNumber = (phoneNumber: string): boolean => {
135
- return typeof phoneNumber === 'string' && /^\+\d{9,15}$/.test(phoneNumber);
136
- };
137
-
138
- export const customExerciseTemplateToExerciseTemplate = (
139
- cet: CustomExercise,
140
- ): ExerciseTemplate => ({
141
- id: cet.id,
142
- title: cet.title,
143
- priority: 10,
144
- muscle_group: cet.muscle_group,
145
- other_muscles: cet.other_muscles || [],
146
- exercise_type: cet.exercise_type,
147
- equipment_category: cet.equipment_category,
148
- is_custom: true,
149
- is_archived: cet.is_archived,
150
- custom_exercise_image_url: cet.custom_exercise_image_url,
151
- thumbnail_url: cet.thumbnail_url,
152
- parent_exercise_template_id: cet.parent_exercise_template_id,
153
- });
154
-
155
- /**
156
- * Return true is value is of format: NN:NN or NN:NN:NN
157
- */
158
- export const isValidFormattedTime = (value: string): boolean => {
159
- if (value.length !== 5 && value.length !== 8) return false;
160
-
161
- if (value.length === 5) {
162
- return (
163
- /[0-9]/.test(value[0]) &&
164
- /[0-9]/.test(value[1]) &&
165
- value[2] === ':' &&
166
- /[0-9]/.test(value[3]) &&
167
- /[0-9]/.test(value[4])
168
- );
169
- }
170
-
171
- if (value.length === 8) {
172
- return (
173
- /[0-9]/.test(value[0]) &&
174
- /[0-9]/.test(value[1]) &&
175
- value[2] === ':' &&
176
- /[0-9]/.test(value[3]) &&
177
- /[0-9]/.test(value[4]) &&
178
- value[5] === ':' &&
179
- /[0-9]/.test(value[6]) &&
180
- /[0-9]/.test(value[7])
181
- );
182
- }
183
-
184
- return false;
185
- };
186
-
187
- export const formatDurationInput = (value: string): string | undefined => {
188
- if (isValidFormattedTime(value)) return value;
189
-
190
- if (!/^[0-9:]+$/i.test(value)) {
191
- return undefined;
192
- }
193
-
194
- if (value.length === 0) {
195
- return value;
196
- }
197
-
198
- if (value === '0') {
199
- return '';
200
- }
201
-
202
- if (value.length === 1) {
203
- return `00:0${value}`;
204
- }
205
-
206
- if (
207
- value.length === 4 &&
208
- value[0] === '0' &&
209
- value[1] === '0' &&
210
- value[3] === '0'
211
- ) {
212
- // 00:0 -> ''
213
- return '';
214
- }
215
-
216
- if (value.length === 4) {
217
- // backspace
218
- // 12:3 -> 01:23
219
- return `0${value[0]}:${value[1]}${value[3]}`;
220
- }
221
-
222
- if (value.length === 6 && value[0] === '0') {
223
- // 01:234 -> 12:34
224
- return `${value[1]}${value[3]}:${value[4]}${value[5]}`;
225
- }
226
-
227
- if (value.length === 7 && value[0] === '0') {
228
- // backspace
229
- // 01:23:4 -> 12:34
230
- return `${value[1]}${value[3]}:${value[4]}${value[6]}`;
231
- }
232
-
233
- if (value.length === 7) {
234
- // backspace
235
- // 12:34:5 -> 01:23:45
236
- return `0${value[0]}:${value[1]}${value[3]}:${value[4]}${value[6]}`;
237
- }
238
-
239
- if (value.length === 6 && value[0] !== '0') {
240
- // 12:345 -> 01:23:45
241
- return `0${value[0]}:${value[1]}${value[3]}:${value[4]}${value[5]}`;
242
- }
243
-
244
- if (value.length === 9) {
245
- // 01:23:456 -> 12:34:56
246
- return `${value[1]}${value[3]}:${value[4]}${value[6]}:${value[7]}${value[8]}`;
247
- }
248
-
249
- return undefined;
250
- };
251
-
252
- /**
253
- * Will always return a number. Ivalid strings return 0
254
- */
255
- export const forceStringToNumber = (value?: string): number => {
256
- const numOrNaN = Number(value ? value.replace(',', '.') : 0);
257
-
258
- return numOrNaN ? numOrNaN : 0;
259
- };
260
-
261
- /**
262
- * Returns a number or undefined
263
- */
264
- export const stringToNumber = (value?: string): number | undefined => {
265
- if (!value) return undefined;
266
-
267
- const numOrNaN = Number(value ? value.replace(',', '.') : 0);
268
-
269
- return isNaN(numOrNaN) ? undefined : numOrNaN;
270
- };
271
-
272
- export const getClosestDataPointBeforeTargetDate = <T>(
273
- data: T[],
274
- dateExtractor: (item: T) => Date,
275
- targetDate: Date,
276
- ): T | undefined => {
277
- if (!data.length) return undefined;
278
-
279
- let lowerIndex = 0;
280
- let upperIndex = data.length - 1;
281
- let middleIndex;
282
-
283
- do {
284
- middleIndex = Math.floor((lowerIndex + upperIndex) / 2);
285
- const middle = data[middleIndex];
286
-
287
- if (dateExtractor(middle) < targetDate) {
288
- lowerIndex = middleIndex + 1;
289
- continue;
290
- } else if (dateExtractor(middle) > targetDate) {
291
- upperIndex = middleIndex - 1;
292
- continue;
293
- } else {
294
- // found exact match
295
- return middle;
296
- }
297
- } while (lowerIndex <= upperIndex);
298
-
299
- if (dateExtractor(data[middleIndex]) < targetDate) {
300
- // the last closest match that was found is before the target
301
- return data[middleIndex];
302
- } else {
303
- // the last closest match that was found is just after the target, so take the previous one
304
- return data[middleIndex - 1] ?? undefined;
305
- }
306
- };
307
-
308
- export const removeAccents = (str: string): string => {
309
- return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
310
- };
311
-
312
- /**
313
- * Calculate the total duration of a workout in seconds
314
- */
315
-
316
- export interface DurationCalculatetableWorkout {
317
- end_time: number;
318
- start_time: number;
319
- }
320
-
321
- export const workoutDurationSeconds = (
322
- workout: DurationCalculatetableWorkout,
323
- ): number => {
324
- const duration = workout.end_time - workout.start_time;
325
- return duration >= 0 ? duration : 0;
326
- };
327
-
328
- /**
329
- * Calculate the total reps in a workout
330
- */
331
-
332
- export interface TotalRepsCalculatetableWorkout {
333
- exercises: Array<{
334
- sets: Array<{
335
- reps?: number | null;
336
- }>;
337
- }>;
338
- }
339
-
340
- export const workoutReps = (
341
- workout: TotalRepsCalculatetableWorkout,
342
- ): number => {
343
- return workout.exercises.reduce((exerciseAccu, exercise) => {
344
- const exerciseReps = exercise.sets.reduce((setAccu, set) => {
345
- if (set.reps) {
346
- return setAccu + set.reps;
347
- }
348
-
349
- return setAccu;
350
- }, 0);
351
-
352
- return exerciseAccu + exerciseReps;
353
- }, 0);
354
- };
355
-
356
- /**
357
- * Calculate the total distance in a workout in meters
358
- */
359
-
360
- export interface TotalDistanceCalculatetableWorkout {
361
- exercises: Array<{
362
- sets: Array<{
363
- distance_meters?: number | null;
364
- }>;
365
- }>;
366
- }
367
-
368
- export const workoutDistanceMeters = (
369
- workout: TotalDistanceCalculatetableWorkout,
370
- ): number => {
371
- return workout.exercises.reduce((exerciseAccu, exercise) => {
372
- const exerciseDistance = exercise.sets.reduce((setAccu, set) => {
373
- if (set.distance_meters) {
374
- return setAccu + set.distance_meters;
375
- }
376
-
377
- return setAccu;
378
- }, 0);
379
-
380
- return exerciseAccu + exerciseDistance;
381
- }, 0);
382
- };
383
-
384
- /**
385
- * Calculate the total set count in a workout
386
- */
387
-
388
- export interface TotalSetCountWorkout {
389
- exercises: Array<{ sets: Array<any> }>;
390
- }
391
-
392
- export const workoutSetCount = (w: TotalSetCountWorkout): number => {
393
- return w.exercises.reduce((accu, exercise) => {
394
- return accu + exercise.sets.length;
395
- }, 0);
396
- };
397
-
398
- export const oneRepMaxPercentageMap: { [s: number]: number } = {
399
- 1: 1.0,
400
- 2: 0.97,
401
- 3: 0.94,
402
- 4: 0.92,
403
- 5: 0.89,
404
- 6: 0.86,
405
- 7: 0.83,
406
- 8: 0.81,
407
- 9: 0.78,
408
- 10: 0.75,
409
- 11: 0.73,
410
- 12: 0.71,
411
- 13: 0.7,
412
- 14: 0.68,
413
- 15: 0.67,
414
- 16: 0.65,
415
- 17: 0.64,
416
- 18: 0.63,
417
- 19: 0.61,
418
- 20: 0.6,
419
- 21: 0.59,
420
- 22: 0.58,
421
- 23: 0.57,
422
- 24: 0.56,
423
- 25: 0.55,
424
- 26: 0.54,
425
- 27: 0.53,
426
- 28: 0.52,
427
- 29: 0.51,
428
- 30: 0.5,
429
- };
430
-
431
- export const oneRepMax = (weight: number, reps: number): number => {
432
- if (reps === 0) {
433
- return 0;
434
- }
435
-
436
- const percent = reps > 30 ? 0.5 : (oneRepMaxPercentageMap[reps] as number);
437
-
438
- return weight / percent;
439
- };
440
-
441
- export const setVolume = (weight: number, reps: number): number => {
442
- return weight * reps;
443
- };
444
-
445
- export const numberWithCommas = (x: number): string => {
446
- return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
447
- };
448
-
449
- export const comparableExerciseTemplateIds = new Set([
450
- '79D0BB3A' /* Bench Press (Barbell) **/,
451
- 'D04AC939' /* Squat (Barbell) **/,
452
- 'A5AC6449' /* Bicep Curl (Barbell) **/,
453
- '5F4E6DD3' /* Deadlift (Barbell) **/,
454
- 'D20D7BBE' /* Sumo Deadlift **/,
455
- '50DFDFAB' /* Incline Bench (Barbell) **/,
456
- '55E6546F' /* Penlay Row **/,
457
- 'C6272009' /* Deadlift (Barbell) **/,
458
- '7B8D84E8' /* Overhead Press (Barbell) **/,
459
- 'C7973E0E' /* Leg Press **/,
460
- 'DA0F0470' /* Decline Bench (Barbell) **/,
461
- '5046D0A9' /* Front Squat **/,
462
- '1283BBA6' /* Full Squat **/,
463
- '6622E5A0' /* Sumo Squat (Barbell) **/,
464
- 'FE389074' /* Rack Pull **/,
465
- '92B8C7E1' /* Hip Thrust **/,
466
- '1B2B1E7C' /* Pull Up **/,
467
- '392887AA' /* Push Up **/,
468
- '29083183' /* Chin Up **/,
469
- '7AB9A362' /* Upright Row (Barbell) **/,
470
- '90E506D5' /* Thruster (Barbell) **/,
471
- '2A48E443' /* Straight Leg Deadlift **/,
472
- '073032BB' /* Standing Military Press (Barbell) **/,
473
- '9694DA61' /* Squat (Bodyweight) **/,
474
- 'FB09C938' /* Snatch **/,
475
- '875F585F' /* Skullcrusher (Barbell) **/,
476
- '022DF610' /* Sit Up **/,
477
- '0B841777' /* Shrug (Barbell) **/,
478
- '91AF29E0' /* Seated Overhead Press (Barbell) **/,
479
- '2B4B7310' /* Romanian Deadlift (Barbell) **/,
480
- '818BA121' /* Reverse Lunge (Barbell) **/,
481
- '542F3CD5' /* Push Press **/,
482
- 'E22F9358' /* Power Snatch **/,
483
- 'C628D768' /* Power Clean **/,
484
- '3FF6A22E' /* Pistol Squat **/,
485
- '0EFE8162' /* Pike Pushup **/,
486
- '018ADC12' /* Pendlay Row (Barbell) **/,
487
- '6E6EE645' /* Lunge (Barbell) **/,
488
- '5E1A7777' /* Lunge **/,
489
- 'B74A95BB' /* Kneeling Push Up **/,
490
- '70D4EBBF' /* Jump Squat **/,
491
- 'D57C2EC7' /* Hip Thrust (Barbell) **/,
492
- '4180C405' /* Good Morning (Barbell) **/,
493
- '6575F52D' /* Diamond Push Up **/,
494
- 'DCF3B31B' /* Crunch **/,
495
- '652FEA39' /* Clean Pull **/,
496
- 'D3095577' /* Clean and Press **/,
497
- '9E09CEC3' /* Clean and Jerk **/,
498
- 'ABB00838' /* Clean **/,
499
- 'BB792A36' /* Burpee **/,
500
- 'E644F828' /* Bench Press - Wide Grip (Barbell) **/,
501
- '35B51B87' /* Bench Press - Close Grip (Barbell) **/,
502
- ]);
503
-
504
- export const isCompareExerciseSupported = (temlpateId: string) => {
505
- return comparableExerciseTemplateIds.has(temlpateId);
506
- };
507
-
508
- export const isExerciseTemplate = (x: any): x is ExerciseTemplate => {
509
- return (
510
- x.id !== undefined &&
511
- x.title !== undefined &&
512
- x.muscle_group !== undefined &&
513
- x.exercise_type !== undefined &&
514
- x.equipment_category !== undefined
515
- );
516
- };
package/src/websocket.ts DELETED
@@ -1,36 +0,0 @@
1
- import {
2
- NewMessageChatUpdate,
3
- MessageDeletedChatUpdate,
4
- NewConversationChatUpdate,
5
- } from './chat';
6
-
7
- export interface PingChatUpdate {
8
- type: 'websocket-ping';
9
- }
10
- export interface PongChatUpdate {
11
- type: 'websocket-pong';
12
- }
13
- export interface CoachesClientsUpdated {
14
- type: 'coaches-clients-updated';
15
- new_client_ids: string[];
16
- removed_client_ids: string[];
17
- }
18
-
19
- export interface WebsocketMessageRequest {
20
- message: HevyWebsocketMessage;
21
- recipient_user_ids: string[];
22
- }
23
-
24
- export type HevyWebsocketMessage =
25
- | NewMessageChatUpdate
26
- | MessageDeletedChatUpdate
27
- | NewConversationChatUpdate
28
- | PingChatUpdate
29
- | PongChatUpdate
30
- | CoachesClientsUpdated;
31
-
32
- export const isHevyWebsocketMessage = (
33
- message?: any,
34
- ): message is HevyWebsocketMessage => {
35
- return !!message && typeof message.type === 'string';
36
- };