@dofek/training 0.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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +123 -0
  3. package/dist/activity-icons.d.ts +10 -0
  4. package/dist/activity-icons.d.ts.map +1 -0
  5. package/dist/activity-icons.js +102 -0
  6. package/dist/activity-icons.js.map +1 -0
  7. package/dist/climbing-grades.d.ts +8 -0
  8. package/dist/climbing-grades.d.ts.map +1 -0
  9. package/dist/climbing-grades.js +80 -0
  10. package/dist/climbing-grades.js.map +1 -0
  11. package/dist/derived-cardio.d.ts +17 -0
  12. package/dist/derived-cardio.d.ts.map +1 -0
  13. package/dist/derived-cardio.js +84 -0
  14. package/dist/derived-cardio.js.map +1 -0
  15. package/dist/endurance-types.d.ts +21 -0
  16. package/dist/endurance-types.d.ts.map +1 -0
  17. package/dist/endurance-types.js +39 -0
  18. package/dist/endurance-types.js.map +1 -0
  19. package/dist/grade-adjusted-pace.d.ts +34 -0
  20. package/dist/grade-adjusted-pace.d.ts.map +1 -0
  21. package/dist/grade-adjusted-pace.js +41 -0
  22. package/dist/grade-adjusted-pace.js.map +1 -0
  23. package/dist/muscle-groups.d.ts +35 -0
  24. package/dist/muscle-groups.d.ts.map +1 -0
  25. package/dist/muscle-groups.js +135 -0
  26. package/dist/muscle-groups.js.map +1 -0
  27. package/dist/pmc.d.ts +77 -0
  28. package/dist/pmc.d.ts.map +1 -0
  29. package/dist/pmc.js +83 -0
  30. package/dist/pmc.js.map +1 -0
  31. package/dist/power-analysis.d.ts +82 -0
  32. package/dist/power-analysis.d.ts.map +1 -0
  33. package/dist/power-analysis.js +162 -0
  34. package/dist/power-analysis.js.map +1 -0
  35. package/dist/training-distribution.d.ts +43 -0
  36. package/dist/training-distribution.d.ts.map +1 -0
  37. package/dist/training-distribution.js +68 -0
  38. package/dist/training-distribution.js.map +1 -0
  39. package/dist/training-load.d.ts +66 -0
  40. package/dist/training-load.d.ts.map +1 -0
  41. package/dist/training-load.js +89 -0
  42. package/dist/training-load.js.map +1 -0
  43. package/dist/training.d.ts +61 -0
  44. package/dist/training.d.ts.map +1 -0
  45. package/dist/training.js +564 -0
  46. package/dist/training.js.map +1 -0
  47. package/dist/workout-recommendation.d.ts +86 -0
  48. package/dist/workout-recommendation.d.ts.map +1 -0
  49. package/dist/workout-recommendation.js +338 -0
  50. package/dist/workout-recommendation.js.map +1 -0
  51. package/package.json +52 -0
@@ -0,0 +1,564 @@
1
+ export const OTHER_ACTIVITY_TYPE = "__other__";
2
+ // ============================================================
3
+ // Canonical activity types
4
+ // ============================================================
5
+ /**
6
+ * Superset of all valid internal activity types across all providers.
7
+ * Providers map their domain-specific types to one of these canonical values.
8
+ */
9
+ export const CANONICAL_ACTIVITY_TYPES = [
10
+ "cycling",
11
+ "road_cycling",
12
+ "mountain_biking",
13
+ "gravel_cycling",
14
+ "indoor_cycling",
15
+ "virtual_cycling",
16
+ "e_bike_cycling",
17
+ "cyclocross",
18
+ "track_cycling",
19
+ "bmx",
20
+ "running",
21
+ "swimming",
22
+ "walking",
23
+ "hiking",
24
+ "strength",
25
+ "strength_training",
26
+ "functional_strength",
27
+ "yoga",
28
+ "pilates",
29
+ "elliptical",
30
+ "rowing",
31
+ "skiing",
32
+ "skating",
33
+ "climbing",
34
+ "cardio",
35
+ "hiit",
36
+ "cross_training",
37
+ "trail_running",
38
+ "stair_climbing",
39
+ "stairmaster",
40
+ "stretching",
41
+ "dance",
42
+ "martial_arts",
43
+ "tennis",
44
+ "basketball",
45
+ "soccer",
46
+ "golf",
47
+ "ice_hockey",
48
+ "snowboarding",
49
+ "rock_climbing",
50
+ "surfing",
51
+ "kayaking",
52
+ "functional_fitness",
53
+ "bootcamp",
54
+ "boxing",
55
+ "core",
56
+ "aqua_fitness",
57
+ "circuit_training",
58
+ "group_exercise",
59
+ "triathlon",
60
+ "dancing",
61
+ // Apple Health granular types
62
+ "american_football",
63
+ "archery",
64
+ "australian_football",
65
+ "badminton",
66
+ "baseball",
67
+ "bowling",
68
+ "cricket",
69
+ "curling",
70
+ "equestrian",
71
+ "fencing",
72
+ "fishing",
73
+ "gymnastics",
74
+ "handball",
75
+ "hockey",
76
+ "hunting",
77
+ "lacrosse",
78
+ "mind_and_body",
79
+ "mixed_cardio",
80
+ "paddle_sports",
81
+ "play",
82
+ "preparation_and_recovery",
83
+ "racquetball",
84
+ "rugby",
85
+ "sailing",
86
+ "snow_sports",
87
+ "softball",
88
+ "squash",
89
+ "table_tennis",
90
+ "track_and_field",
91
+ "volleyball",
92
+ "water_fitness",
93
+ "water_polo",
94
+ "water_sports",
95
+ "wrestling",
96
+ "barre",
97
+ "core_training",
98
+ "flexibility",
99
+ "jump_rope",
100
+ "kickboxing",
101
+ "stairs",
102
+ "step_training",
103
+ "wheelchair_walk",
104
+ "wheelchair_run",
105
+ "tai_chi",
106
+ "mixed_metabolic_cardio",
107
+ "hand_cycling",
108
+ "disc_sports",
109
+ "fitness_gaming",
110
+ "cardio_dance",
111
+ "social_dance",
112
+ "paddle_racquet",
113
+ "cooldown",
114
+ "transition",
115
+ "underwater_diving",
116
+ "cross_country_skiing",
117
+ "downhill_skiing",
118
+ // Provider-specific types (Garmin, Peloton, Komoot, Decathlon)
119
+ "breathwork",
120
+ "meditation",
121
+ "paddleboarding",
122
+ "snowshoeing",
123
+ "pickleball",
124
+ "padel",
125
+ "football",
126
+ "multisport",
127
+ "disc_golf",
128
+ "skydiving",
129
+ "paragliding",
130
+ "diving",
131
+ "snorkeling",
132
+ "navigation",
133
+ "geocaching",
134
+ "paddling",
135
+ "gym",
136
+ "open_water_swimming",
137
+ "other",
138
+ ];
139
+ // ============================================================
140
+ // Cycling activity types
141
+ // ============================================================
142
+ /**
143
+ * All canonical activity types considered "cycling" for consolidated views.
144
+ * Individual activities show the specific subtype, but aggregated views
145
+ * (weekly volume, PMC, cycling page) treat them as one sport.
146
+ */
147
+ export const CYCLING_ACTIVITY_TYPES = [
148
+ "cycling",
149
+ "road_cycling",
150
+ "mountain_biking",
151
+ "gravel_cycling",
152
+ "indoor_cycling",
153
+ "virtual_cycling",
154
+ "e_bike_cycling",
155
+ "cyclocross",
156
+ "track_cycling",
157
+ "bmx",
158
+ "hand_cycling",
159
+ ];
160
+ /** Check whether an activity type is a cycling variant. */
161
+ export function isCyclingActivity(activityType) {
162
+ return CYCLING_ACTIVITY_TYPES.some((t) => t === activityType);
163
+ }
164
+ /** Cadence unit for display based on activity type. */
165
+ export function cadenceUnit(activityType) {
166
+ return isCyclingActivity(activityType) ? "rpm" : "steps/min";
167
+ }
168
+ /** Y-axis label for cadence charts. */
169
+ export function cadenceAxisLabel(activityType) {
170
+ return `Cadence (${cadenceUnit(activityType)})`;
171
+ }
172
+ // ============================================================
173
+ // Endurance activity types
174
+ // ============================================================
175
+ // ============================================================
176
+ // Activity type mapper factory
177
+ // ============================================================
178
+ /**
179
+ * Creates a mapping function from provider-specific types to canonical types.
180
+ * Unknown provider types default to "other".
181
+ */
182
+ export function createActivityTypeMapper(providerMappings) {
183
+ return (providerType) => {
184
+ return providerMappings[providerType] ?? "other";
185
+ };
186
+ }
187
+ // ============================================================
188
+ // Provider-specific mapping constants
189
+ // ============================================================
190
+ /** Strava sport_type → canonical activity type */
191
+ export const STRAVA_ACTIVITY_TYPE_MAP = {
192
+ Ride: "road_cycling",
193
+ VirtualRide: "virtual_cycling",
194
+ MountainBikeRide: "mountain_biking",
195
+ GravelRide: "gravel_cycling",
196
+ EBikeRide: "e_bike_cycling",
197
+ EMountainBikeRide: "e_bike_cycling",
198
+ Run: "running",
199
+ VirtualRun: "running",
200
+ TrailRun: "running",
201
+ Walk: "walking",
202
+ Hike: "hiking",
203
+ Swim: "swimming",
204
+ WeightTraining: "strength",
205
+ Yoga: "yoga",
206
+ Rowing: "rowing",
207
+ Canoeing: "rowing",
208
+ Kayaking: "rowing",
209
+ Elliptical: "elliptical",
210
+ NordicSki: "skiing",
211
+ AlpineSki: "skiing",
212
+ BackcountrySki: "skiing",
213
+ Snowboard: "skiing",
214
+ IceSkate: "skating",
215
+ RollerSki: "skiing",
216
+ Crossfit: "strength",
217
+ RockClimbing: "climbing",
218
+ };
219
+ /** Wahoo workout_type_id (numeric) → canonical activity type */
220
+ export const WAHOO_WORKOUT_TYPE_MAP = {
221
+ 0: "cycling",
222
+ 1: "running",
223
+ 2: "running", // treadmill
224
+ 3: "indoor_cycling",
225
+ 4: "mountain_biking",
226
+ 5: "gravel_cycling",
227
+ 6: "swimming",
228
+ 7: "yoga",
229
+ 8: "walking",
230
+ 9: "hiking",
231
+ 10: "rowing",
232
+ 11: "strength",
233
+ 12: "elliptical",
234
+ 13: "skiing",
235
+ };
236
+ /** Polar sport (lowercased) → canonical activity type */
237
+ export const POLAR_SPORT_MAP = {
238
+ running: "running",
239
+ cycling: "cycling",
240
+ swimming: "swimming",
241
+ walking: "walking",
242
+ hiking: "hiking",
243
+ strength_training: "strength",
244
+ yoga: "yoga",
245
+ pilates: "pilates",
246
+ cross_country_skiing: "cross_country_skiing",
247
+ rowing: "rowing",
248
+ elliptical: "elliptical",
249
+ mountain_biking: "mountain_biking",
250
+ trail_running: "trail_running",
251
+ cross_training: "cross_training",
252
+ group_exercise: "group_exercise",
253
+ stretching: "stretching",
254
+ dance: "dance",
255
+ martial_arts: "martial_arts",
256
+ tennis: "tennis",
257
+ basketball: "basketball",
258
+ soccer: "soccer",
259
+ golf: "golf",
260
+ ice_hockey: "ice_hockey",
261
+ skiing: "skiing",
262
+ snowboarding: "snowboarding",
263
+ skating: "skating",
264
+ rock_climbing: "rock_climbing",
265
+ surfing: "surfing",
266
+ kayaking: "kayaking",
267
+ functional_training: "functional_fitness",
268
+ bootcamp: "bootcamp",
269
+ boxing: "boxing",
270
+ core: "core",
271
+ aqua_fitness: "aqua_fitness",
272
+ circuit_training: "circuit_training",
273
+ triathlon: "triathlon",
274
+ indoor_cycling: "indoor_cycling",
275
+ indoor_rowing: "rowing",
276
+ indoor_running: "running",
277
+ indoor_walking: "walking",
278
+ treadmill_running: "running",
279
+ stair_climbing: "stairmaster",
280
+ };
281
+ /** Garmin activityType → canonical activity type */
282
+ export const GARMIN_ACTIVITY_TYPE_MAP = {
283
+ // Running
284
+ RUNNING: "running",
285
+ TRAIL_RUNNING: "running",
286
+ TREADMILL_RUNNING: "running",
287
+ TRACK_RUNNING: "running",
288
+ // Cycling
289
+ CYCLING: "cycling",
290
+ MOUNTAIN_BIKING: "mountain_biking",
291
+ ROAD_BIKING: "road_cycling",
292
+ INDOOR_CYCLING: "indoor_cycling",
293
+ GRAVEL_CYCLING: "gravel_cycling",
294
+ VIRTUAL_RIDE: "virtual_cycling",
295
+ // Swimming
296
+ SWIMMING: "swimming",
297
+ LAP_SWIMMING: "swimming",
298
+ OPEN_WATER_SWIMMING: "swimming",
299
+ // Walking / Hiking
300
+ WALKING: "walking",
301
+ HIKING: "hiking",
302
+ // Strength / Cardio
303
+ STRENGTH_TRAINING: "strength",
304
+ INDOOR_CARDIO: "cardio",
305
+ // Other fitness
306
+ YOGA: "yoga",
307
+ PILATES: "pilates",
308
+ ELLIPTICAL: "elliptical",
309
+ ROWING: "rowing",
310
+ };
311
+ /** Oura activity (lowercased) → canonical activity type */
312
+ export const OURA_ACTIVITY_TYPE_MAP = {
313
+ walking: "walking",
314
+ running: "running",
315
+ cycling: "cycling",
316
+ swimming: "swimming",
317
+ hiking: "hiking",
318
+ yoga: "yoga",
319
+ elliptical: "elliptical",
320
+ rowing: "rowing",
321
+ strength_training: "strength",
322
+ weight_training: "strength",
323
+ dancing: "dancing",
324
+ pilates: "pilates",
325
+ indoor_cycling: "indoor_cycling",
326
+ stairmaster: "stairmaster",
327
+ other: "other",
328
+ };
329
+ /** RideWithGPS activity_type → canonical activity type */
330
+ export const RIDE_WITH_GPS_ACTIVITY_TYPE_MAP = {
331
+ cycling: "cycling",
332
+ "cycling:generic": "cycling",
333
+ "cycling:road": "road_cycling",
334
+ "cycling:mountain": "mountain_biking",
335
+ "cycling:gravel": "gravel_cycling",
336
+ "cycling:cyclocross": "cyclocross",
337
+ "cycling:track": "track_cycling",
338
+ mountain_biking: "mountain_biking",
339
+ road_cycling: "road_cycling",
340
+ gravel_cycling: "gravel_cycling",
341
+ cyclocross: "cyclocross",
342
+ track_cycling: "track_cycling",
343
+ running: "running",
344
+ trail_running: "running",
345
+ walking: "walking",
346
+ hiking: "hiking",
347
+ swimming: "swimming",
348
+ };
349
+ /** Apple Health HKWorkoutActivityType → normalized lowercase name */
350
+ export const APPLE_HEALTH_WORKOUT_TYPE_MAP = {
351
+ HKWorkoutActivityTypeAmericanFootball: "american_football",
352
+ HKWorkoutActivityTypeArchery: "archery",
353
+ HKWorkoutActivityTypeAustralianFootball: "australian_football",
354
+ HKWorkoutActivityTypeBadminton: "badminton",
355
+ HKWorkoutActivityTypeBaseball: "baseball",
356
+ HKWorkoutActivityTypeBasketball: "basketball",
357
+ HKWorkoutActivityTypeBowling: "bowling",
358
+ HKWorkoutActivityTypeBoxing: "boxing",
359
+ HKWorkoutActivityTypeClimbing: "climbing",
360
+ HKWorkoutActivityTypeCricket: "cricket",
361
+ HKWorkoutActivityTypeCrossCountrySkiing: "cross_country_skiing",
362
+ HKWorkoutActivityTypeCrossTraining: "cross_training",
363
+ HKWorkoutActivityTypeCurling: "curling",
364
+ HKWorkoutActivityTypeCycling: "cycling",
365
+ HKWorkoutActivityTypeDance: "dance",
366
+ HKWorkoutActivityTypeDownhillSkiing: "downhill_skiing",
367
+ HKWorkoutActivityTypeElliptical: "elliptical",
368
+ HKWorkoutActivityTypeEquestrianSports: "equestrian",
369
+ HKWorkoutActivityTypeFencing: "fencing",
370
+ HKWorkoutActivityTypeFishing: "fishing",
371
+ HKWorkoutActivityTypeFunctionalStrengthTraining: "functional_strength",
372
+ HKWorkoutActivityTypeGolf: "golf",
373
+ HKWorkoutActivityTypeGymnastics: "gymnastics",
374
+ HKWorkoutActivityTypeHandball: "handball",
375
+ HKWorkoutActivityTypeHiking: "hiking",
376
+ HKWorkoutActivityTypeHockey: "hockey",
377
+ HKWorkoutActivityTypeHunting: "hunting",
378
+ HKWorkoutActivityTypeLacrosse: "lacrosse",
379
+ HKWorkoutActivityTypeMartialArts: "martial_arts",
380
+ HKWorkoutActivityTypeMindAndBody: "mind_and_body",
381
+ HKWorkoutActivityTypeMixedCardio: "mixed_cardio",
382
+ HKWorkoutActivityTypePaddleSports: "paddle_sports",
383
+ HKWorkoutActivityTypePlay: "play",
384
+ HKWorkoutActivityTypePreparationAndRecovery: "preparation_and_recovery",
385
+ HKWorkoutActivityTypeRacquetball: "racquetball",
386
+ HKWorkoutActivityTypeRowing: "rowing",
387
+ HKWorkoutActivityTypeRugby: "rugby",
388
+ HKWorkoutActivityTypeRunning: "running",
389
+ HKWorkoutActivityTypeSailing: "sailing",
390
+ HKWorkoutActivityTypeSkatingSports: "skating",
391
+ HKWorkoutActivityTypeSnowSports: "snow_sports",
392
+ HKWorkoutActivityTypeSoccer: "soccer",
393
+ HKWorkoutActivityTypeSoftball: "softball",
394
+ HKWorkoutActivityTypeSquash: "squash",
395
+ HKWorkoutActivityTypeStairClimbing: "stair_climbing",
396
+ HKWorkoutActivityTypeSurfingSports: "surfing",
397
+ HKWorkoutActivityTypeSwimming: "swimming",
398
+ HKWorkoutActivityTypeTableTennis: "table_tennis",
399
+ HKWorkoutActivityTypeTennis: "tennis",
400
+ HKWorkoutActivityTypeTrackAndField: "track_and_field",
401
+ HKWorkoutActivityTypeTraditionalStrengthTraining: "strength_training",
402
+ HKWorkoutActivityTypeVolleyball: "volleyball",
403
+ HKWorkoutActivityTypeWalking: "walking",
404
+ HKWorkoutActivityTypeWaterFitness: "water_fitness",
405
+ HKWorkoutActivityTypeWaterPolo: "water_polo",
406
+ HKWorkoutActivityTypeWaterSports: "water_sports",
407
+ HKWorkoutActivityTypeWrestling: "wrestling",
408
+ HKWorkoutActivityTypeYoga: "yoga",
409
+ HKWorkoutActivityTypeBarre: "barre",
410
+ HKWorkoutActivityTypeCoreTraining: "core_training",
411
+ HKWorkoutActivityTypeFlexibility: "flexibility",
412
+ HKWorkoutActivityTypeHighIntensityIntervalTraining: "hiit",
413
+ HKWorkoutActivityTypeJumpRope: "jump_rope",
414
+ HKWorkoutActivityTypeKickboxing: "kickboxing",
415
+ HKWorkoutActivityTypePilates: "pilates",
416
+ HKWorkoutActivityTypeSnowboarding: "snowboarding",
417
+ HKWorkoutActivityTypeStairs: "stairs",
418
+ HKWorkoutActivityTypeStepTraining: "step_training",
419
+ HKWorkoutActivityTypeWheelchairWalkPace: "wheelchair_walk",
420
+ HKWorkoutActivityTypeWheelchairRunPace: "wheelchair_run",
421
+ HKWorkoutActivityTypeTaiChi: "tai_chi",
422
+ HKWorkoutActivityTypeMixedMetabolicCardioTraining: "mixed_metabolic_cardio",
423
+ HKWorkoutActivityTypeHandCycling: "hand_cycling",
424
+ HKWorkoutActivityTypeDiscSports: "disc_sports",
425
+ HKWorkoutActivityTypeFitnessGaming: "fitness_gaming",
426
+ HKWorkoutActivityTypeCardioDance: "cardio_dance",
427
+ HKWorkoutActivityTypeSocialDance: "social_dance",
428
+ HKWorkoutActivityTypePickleball: "paddle_racquet",
429
+ HKWorkoutActivityTypeCooldown: "cooldown",
430
+ HKWorkoutActivityTypeSwimBikeRun: "triathlon",
431
+ HKWorkoutActivityTypeTransition: "transition",
432
+ HKWorkoutActivityTypeUnderwaterDiving: "underwater_diving",
433
+ HKWorkoutActivityTypeOther: "other",
434
+ };
435
+ const ACTIVITY_TYPE_LABELS = {
436
+ cycling: "Cycling",
437
+ road_cycling: "Road Cycling",
438
+ mountain_biking: "Mountain Biking",
439
+ gravel_cycling: "Gravel Cycling",
440
+ indoor_cycling: "Indoor Cycling",
441
+ virtual_cycling: "Virtual Cycling",
442
+ e_bike_cycling: "E-Bike Cycling",
443
+ cyclocross: "Cyclocross",
444
+ track_cycling: "Track Cycling",
445
+ bmx: "BMX",
446
+ running: "Running",
447
+ trail_running: "Trail Running",
448
+ walking: "Walking",
449
+ hiking: "Hiking",
450
+ swimming: "Swimming",
451
+ rowing: "Rowing",
452
+ yoga: "Yoga",
453
+ pilates: "Pilates",
454
+ elliptical: "Elliptical",
455
+ strength: "Strength",
456
+ strength_training: "Strength Training",
457
+ functional_strength: "Functional Strength",
458
+ stair_climbing: "Stair Climbing",
459
+ cross_training: "Cross Training",
460
+ hiit: "HIIT",
461
+ cardio: "Cardio",
462
+ other: "Other",
463
+ };
464
+ const VERTICAL_ASCENT_ACTIVITY_TYPE_GROUP_LABELS = {
465
+ road_cycling: "Road Cycling",
466
+ mountain_biking: "Mountain Biking",
467
+ gravel_cycling: "Gravel Cycling",
468
+ other_cycling: "Other Cycling",
469
+ };
470
+ function toTitleCase(value) {
471
+ return value
472
+ .split(/[_\-\s]+/)
473
+ .filter(Boolean)
474
+ .map((word) => {
475
+ const lower = word.toLowerCase();
476
+ if (lower === "hiit")
477
+ return "HIIT";
478
+ return `${lower.charAt(0).toUpperCase()}${lower.slice(1)}`;
479
+ })
480
+ .join(" ");
481
+ }
482
+ function normalizeActivityType(activityType) {
483
+ const normalized = activityType.trim().toLowerCase();
484
+ if (normalized === OTHER_ACTIVITY_TYPE || normalized === "other") {
485
+ return OTHER_ACTIVITY_TYPE;
486
+ }
487
+ return normalized;
488
+ }
489
+ export function formatActivityTypeLabel(activityType) {
490
+ const normalized = normalizeActivityType(activityType);
491
+ if (normalized === OTHER_ACTIVITY_TYPE) {
492
+ return "Other";
493
+ }
494
+ return ACTIVITY_TYPE_LABELS[normalized] ?? toTitleCase(normalized);
495
+ }
496
+ export function getVerticalAscentActivityTypeGroup(activityType) {
497
+ const normalized = normalizeActivityType(activityType);
498
+ if (normalized === "road_cycling")
499
+ return "road_cycling";
500
+ if (normalized === "mountain_biking")
501
+ return "mountain_biking";
502
+ if (normalized === "gravel_cycling")
503
+ return "gravel_cycling";
504
+ return "other_cycling";
505
+ }
506
+ export function formatVerticalAscentActivityTypeGroupLabel(activityTypeGroup) {
507
+ return VERTICAL_ASCENT_ACTIVITY_TYPE_GROUP_LABELS[activityTypeGroup];
508
+ }
509
+ export function collapseWeeklyVolumeActivityTypes(rows, maxLegendItems = 6) {
510
+ if (rows.length === 0)
511
+ return rows;
512
+ // Consolidate all cycling subtypes into generic "cycling" before grouping
513
+ const normalizedRows = rows.map((row) => {
514
+ const normalized = normalizeActivityType(row.activity_type);
515
+ return {
516
+ ...row,
517
+ activity_type: isCyclingActivity(normalized) ? "cycling" : normalized,
518
+ };
519
+ });
520
+ const totalsByType = new Map();
521
+ for (const row of normalizedRows) {
522
+ totalsByType.set(row.activity_type, (totalsByType.get(row.activity_type) ?? 0) + row.hours);
523
+ }
524
+ const orderedTypes = [...totalsByType.entries()]
525
+ .sort((a, b) => b[1] - a[1])
526
+ .map(([type]) => type);
527
+ const shouldCollapse = orderedTypes.length > maxLegendItems;
528
+ const keepTypes = shouldCollapse
529
+ ? new Set(orderedTypes.slice(0, Math.max(0, maxLegendItems - 1)))
530
+ : new Set(orderedTypes);
531
+ const aggregated = new Map();
532
+ for (const row of normalizedRows) {
533
+ const groupedType = keepTypes.has(row.activity_type) ? row.activity_type : OTHER_ACTIVITY_TYPE;
534
+ const key = `${row.week}::${groupedType}`;
535
+ const existing = aggregated.get(key);
536
+ if (existing) {
537
+ existing.count += row.count;
538
+ existing.hours += row.hours;
539
+ continue;
540
+ }
541
+ aggregated.set(key, {
542
+ week: row.week,
543
+ activity_type: groupedType,
544
+ count: row.count,
545
+ hours: row.hours,
546
+ });
547
+ }
548
+ return [...aggregated.values()].sort((a, b) => {
549
+ if (a.week !== b.week)
550
+ return a.week.localeCompare(b.week);
551
+ return a.activity_type.localeCompare(b.activity_type);
552
+ });
553
+ }
554
+ /**
555
+ * Pick the daily load row to display in "current strain" UI.
556
+ * Always returns the latest (most recent) row so the dashboard reflects the
557
+ * current day's rolling workload state.
558
+ */
559
+ export function selectRecentDailyLoad(rows) {
560
+ if (rows.length === 0)
561
+ return null;
562
+ return rows[rows.length - 1] ?? null;
563
+ }
564
+ //# sourceMappingURL=training.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"training.js","sourceRoot":"","sources":["../src/training.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Next workout recommendation engine.
3
+ *
4
+ * Pure function: takes a snapshot of the user's training state and returns
5
+ * an evidence-based recommendation for what to do next.
6
+ *
7
+ * Based on:
8
+ * - Polarized training model (Seiler, Stoggl & Sperlich 2014): 80/20 zone split
9
+ * - Muscle group recovery (Schoenfeld 2016): 48h+ per group, 2x/week target
10
+ * - HRV-guided training (Plews 2013): modulate intensity by recovery
11
+ * - Hard/easy alternation (Seiler 2010): never stack high-intensity days
12
+ * - HIIT capping: max 2-3x/week with 48h spacing
13
+ */
14
+ export type WorkoutType = "rest" | "active_recovery" | "cardio_easy" | "cardio_intervals" | "strength";
15
+ export interface IntervalProtocol {
16
+ name: string;
17
+ description: string;
18
+ workSeconds: number;
19
+ restSeconds: number;
20
+ sets: number;
21
+ targetZone: number;
22
+ warmupMinutes: number;
23
+ cooldownMinutes: number;
24
+ totalMinutes: number;
25
+ }
26
+ export interface StrengthDetail {
27
+ muscleGroups: string[];
28
+ estimatedDurationMinutes: number;
29
+ }
30
+ export interface CardioEasyDetail {
31
+ targetZone: 1 | 2;
32
+ durationMinutes: number;
33
+ targetHrRange: {
34
+ min: number;
35
+ max: number;
36
+ } | null;
37
+ }
38
+ export interface CardioIntervalsDetail {
39
+ protocol: IntervalProtocol;
40
+ targetHrRange: {
41
+ min: number;
42
+ max: number;
43
+ } | null;
44
+ }
45
+ export interface WorkoutRecommendation {
46
+ type: WorkoutType;
47
+ summary: string;
48
+ reasoning: string[];
49
+ strengthDetail: StrengthDetail | null;
50
+ cardioEasyDetail: CardioEasyDetail | null;
51
+ cardioIntervalsDetail: CardioIntervalsDetail | null;
52
+ }
53
+ export interface RecentActivity {
54
+ type: "strength" | "cardio";
55
+ date: string;
56
+ wasHardDay: boolean;
57
+ muscleGroups: string[];
58
+ activityType: string;
59
+ }
60
+ export interface ZoneDistribution {
61
+ zone1Samples: number;
62
+ zone2Samples: number;
63
+ zone3Samples: number;
64
+ zone4Samples: number;
65
+ zone5Samples: number;
66
+ }
67
+ export interface MuscleGroupFreshness {
68
+ muscleGroup: string;
69
+ lastWorkedDate: string;
70
+ setsThisWeek: number;
71
+ }
72
+ export interface RecommendationInput {
73
+ today: string;
74
+ readinessScore: number | null;
75
+ workloadRatio: number | null;
76
+ trainingStressBalance: number | null;
77
+ sleepDebtMinutes: number;
78
+ recentActivities: RecentActivity[];
79
+ zoneDistribution: ZoneDistribution | null;
80
+ muscleGroupFreshness: MuscleGroupFreshness[];
81
+ userMaxHr: number | null;
82
+ userRestingHr: number | null;
83
+ }
84
+ export declare function muscleGroupLabel(group: string): string;
85
+ export declare function recommendNextWorkout(input: RecommendationInput): WorkoutRecommendation;
86
+ //# sourceMappingURL=workout-recommendation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workout-recommendation.d.ts","sourceRoot":"","sources":["../src/workout-recommendation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,iBAAiB,GACjB,aAAa,GACb,kBAAkB,GAClB,UAAU,CAAC;AAEf,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,aAAa,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,qBAAqB,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACrD;AAID,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,cAAc,EAAE,CAAC;IACnC,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,oBAAoB,EAAE,oBAAoB,EAAE,CAAC;IAC7C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAyCD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD;AA8BD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,GAAG,qBAAqB,CA4EtF"}