@swimedge/metrics 1.0.1 → 1.0.2

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/index.d.ts CHANGED
@@ -54,6 +54,7 @@ export interface SwimSession extends SwimSessionLaps {
54
54
  perceivedEffort?: string;
55
55
  equipment?: string[];
56
56
  avgDistancePerStroke?: number;
57
+ routeCoordinates?: number[][];
57
58
  }
58
59
  export interface AggregatedSwimMetrics {
59
60
  totalDistance: number;
@@ -112,6 +113,7 @@ export interface MetricDefinition {
112
113
  watch: boolean;
113
114
  manual: boolean;
114
115
  };
116
+ swimTypes: SwimType[];
115
117
  formatter?: (session: SwimSession) => string | number | null;
116
118
  lapFormatter?: (value: any) => string;
117
119
  condition?: (session: SwimSession) => boolean;
@@ -122,5 +124,8 @@ export declare const MetricsHelper: {
122
124
  getSessionMetrics: () => MetricDefinition[];
123
125
  getLapMetrics: () => MetricDefinition[];
124
126
  getMetricsByCategory: (category: MetricCategory) => MetricDefinition[];
127
+ getMetricsBySwimType: (swimType: SwimType) => MetricDefinition[];
128
+ getIndoorMetrics: () => MetricDefinition[];
129
+ getOpenWaterMetrics: () => MetricDefinition[];
125
130
  getAllMetricKeys: () => string[];
126
131
  };
package/dist/index.js CHANGED
@@ -49,7 +49,7 @@ exports.formatDistance = formatDistance;
49
49
  const formatAverageTurnTime = (session) => {
50
50
  if (!session.lapTurnTimes?.length)
51
51
  return '';
52
- const validTurnTimes = session.lapTurnTimes.filter(t => !isNaN(t) && t > 0);
52
+ const validTurnTimes = session.lapTurnTimes.filter((t) => !isNaN(t) && t > 0);
53
53
  if (!validTurnTimes.length)
54
54
  return '';
55
55
  const avgTime = validTurnTimes.reduce((a, b) => a + b, 0) / validTurnTimes.length;
@@ -69,6 +69,7 @@ exports.METRICS_REGISTRY = {
69
69
  healthKitSource: 'HKWorkout.totalDistance',
70
70
  isHealthKit: true,
71
71
  availableFrom: { watch: true, manual: true },
72
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
72
73
  formatter: (s) => (0, exports.formatDistance)(s.distance),
73
74
  },
74
75
  time: {
@@ -83,6 +84,7 @@ exports.METRICS_REGISTRY = {
83
84
  healthKitSource: 'HKWorkout.duration',
84
85
  isHealthKit: true,
85
86
  availableFrom: { watch: true, manual: true },
87
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
86
88
  formatter: (s) => (0, exports.formatTime)(s.time),
87
89
  },
88
90
  activeTime: {
@@ -97,6 +99,7 @@ exports.METRICS_REGISTRY = {
97
99
  healthKitSource: 'HKWorkoutBuilder.elapsedTime',
98
100
  isHealthKit: true,
99
101
  availableFrom: { watch: true, manual: false },
102
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
100
103
  },
101
104
  restTime: {
102
105
  key: 'restTime',
@@ -108,6 +111,7 @@ exports.METRICS_REGISTRY = {
108
111
  isPerSession: true,
109
112
  formula: 'Total Duration - Active Time',
110
113
  availableFrom: { watch: true, manual: false },
114
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
111
115
  formatter: (s) => (0, exports.formatTime)(s.restTime),
112
116
  condition: (s) => !!s.restTime,
113
117
  },
@@ -121,6 +125,7 @@ exports.METRICS_REGISTRY = {
121
125
  isPerSession: true,
122
126
  formula: 'Distance ÷ Pool Length',
123
127
  availableFrom: { watch: true, manual: true },
128
+ swimTypes: [SwimType.INDOOR],
124
129
  formatter: (s) => String(s.laps),
125
130
  condition: (s) => !!s.laps,
126
131
  },
@@ -134,6 +139,7 @@ exports.METRICS_REGISTRY = {
134
139
  isPerSession: true,
135
140
  formula: '(Active Time ÷ Distance) × 100',
136
141
  availableFrom: { watch: true, manual: true },
142
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
137
143
  formatter: (s) => (0, exports.formatPace)(s.avgPace),
138
144
  condition: (s) => !!s.avgPace,
139
145
  },
@@ -147,6 +153,7 @@ exports.METRICS_REGISTRY = {
147
153
  isPerSession: true,
148
154
  source: 'User configuration setting',
149
155
  availableFrom: { watch: true, manual: true },
156
+ swimTypes: [SwimType.INDOOR],
150
157
  formatter: (s) => `${s.poolLength}m`,
151
158
  condition: (s) => !!s.poolLength,
152
159
  },
@@ -160,6 +167,7 @@ exports.METRICS_REGISTRY = {
160
167
  isPerSession: true,
161
168
  source: 'User selection or workout location type',
162
169
  availableFrom: { watch: true, manual: true },
170
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
163
171
  formatter: (s) => s.swimType,
164
172
  condition: (s) => !!s.swimType,
165
173
  },
@@ -174,6 +182,7 @@ exports.METRICS_REGISTRY = {
174
182
  source: 'Apple Watch water temperature sensor',
175
183
  isHealthKit: true,
176
184
  availableFrom: { watch: true, manual: false },
185
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
177
186
  formatter: (s) => `${(0, exports.formatNumber)(s.waterTemp, 1)}°C`,
178
187
  condition: (s) => !!s.waterTemp,
179
188
  },
@@ -189,6 +198,7 @@ exports.METRICS_REGISTRY = {
189
198
  healthKitSource: 'HKQuantityTypeIdentifierHeartRate',
190
199
  isHealthKit: true,
191
200
  availableFrom: { watch: true, manual: false },
201
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
192
202
  formatter: (s) => (0, exports.formatHeartRate)(s.avgHeartRate),
193
203
  condition: (s) => !!s.avgHeartRate,
194
204
  },
@@ -204,6 +214,7 @@ exports.METRICS_REGISTRY = {
204
214
  healthKitSource: 'HKQuantityTypeIdentifierHeartRate',
205
215
  isHealthKit: true,
206
216
  availableFrom: { watch: true, manual: false },
217
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
207
218
  formatter: (s) => (0, exports.formatHeartRate)(s.maxHeartRate),
208
219
  condition: (s) => !!s.maxHeartRate,
209
220
  },
@@ -219,6 +230,7 @@ exports.METRICS_REGISTRY = {
219
230
  healthKitSource: 'HKQuantityTypeIdentifierHeartRate',
220
231
  isHealthKit: true,
221
232
  availableFrom: { watch: true, manual: false },
233
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
222
234
  formatter: (s) => (0, exports.formatHeartRate)(s.minHeartRate),
223
235
  condition: (s) => !!s.minHeartRate,
224
236
  },
@@ -234,6 +246,7 @@ exports.METRICS_REGISTRY = {
234
246
  healthKitSource: 'HKQuantityTypeIdentifierActiveEnergyBurned',
235
247
  isHealthKit: true,
236
248
  availableFrom: { watch: true, manual: true },
249
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
237
250
  formatter: (s) => String(s.calories),
238
251
  condition: (s) => !!s.calories,
239
252
  },
@@ -249,6 +262,7 @@ exports.METRICS_REGISTRY = {
249
262
  healthKitSource: 'HKQuantityTypeIdentifierSwimmingStrokeCount',
250
263
  isHealthKit: true,
251
264
  availableFrom: { watch: true, manual: false },
265
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
252
266
  formatter: (s) => String(s.totalStrokeCount || 0),
253
267
  condition: (s) => !!s.totalStrokeCount,
254
268
  },
@@ -262,6 +276,7 @@ exports.METRICS_REGISTRY = {
262
276
  isPerSession: true,
263
277
  formula: 'Total Strokes ÷ (Active Time in Minutes)',
264
278
  availableFrom: { watch: true, manual: true },
279
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
265
280
  formatter: (s) => `${(0, exports.formatNumber)(s.avgStrokeRate, 1)} SPM`,
266
281
  condition: (s) => !!s.avgStrokeRate,
267
282
  },
@@ -275,6 +290,7 @@ exports.METRICS_REGISTRY = {
275
290
  isPerSession: true,
276
291
  formula: 'Distance ÷ Total Strokes',
277
292
  availableFrom: { watch: true, manual: true },
293
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
278
294
  formatter: (s) => `${(0, exports.formatNumber)(s.avgDistancePerStroke, 2)}m`,
279
295
  condition: (s) => !!s.avgDistancePerStroke,
280
296
  },
@@ -288,6 +304,7 @@ exports.METRICS_REGISTRY = {
288
304
  isPerSession: true,
289
305
  formula: 'Average of (Lap Time in Seconds + Lap Stroke Count) for each lap',
290
306
  availableFrom: { watch: true, manual: true },
307
+ swimTypes: [SwimType.INDOOR],
291
308
  formatter: (s) => (0, exports.formatNumber)(s.avgSwolfScore, 0),
292
309
  condition: (s) => !!s.avgSwolfScore,
293
310
  },
@@ -303,6 +320,7 @@ exports.METRICS_REGISTRY = {
303
320
  healthKitSource: 'HKWorkoutEvent.metadata[HKMetadataKeySwimmingStrokeStyle]',
304
321
  isHealthKit: true,
305
322
  availableFrom: { watch: true, manual: true },
323
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
306
324
  formatter: (s) => s.primaryStrokeType,
307
325
  condition: (s) => !!s.primaryStrokeType,
308
326
  },
@@ -316,6 +334,7 @@ exports.METRICS_REGISTRY = {
316
334
  isPerLap: true,
317
335
  source: 'Tracked per lap completion',
318
336
  availableFrom: { watch: true, manual: false },
337
+ swimTypes: [SwimType.INDOOR],
319
338
  formatter: (s) => `${s.lapSplitTimes.length} splits`,
320
339
  lapFormatter: (v) => (0, exports.formatTime)(v),
321
340
  condition: (s) => !!s.lapSplitTimes?.length,
@@ -333,6 +352,7 @@ exports.METRICS_REGISTRY = {
333
352
  healthKitSource: 'HKQuantityTypeIdentifierHeartRate',
334
353
  isHealthKit: true,
335
354
  availableFrom: { watch: true, manual: false },
355
+ swimTypes: [SwimType.INDOOR],
336
356
  },
337
357
  lapStrokeCounts: {
338
358
  key: 'lapStrokeCounts',
@@ -347,6 +367,7 @@ exports.METRICS_REGISTRY = {
347
367
  healthKitSource: 'HKQuantityTypeIdentifierSwimmingStrokeCount',
348
368
  isHealthKit: true,
349
369
  availableFrom: { watch: true, manual: false },
370
+ swimTypes: [SwimType.INDOOR],
350
371
  },
351
372
  id: {
352
373
  key: 'id',
@@ -355,6 +376,7 @@ exports.METRICS_REGISTRY = {
355
376
  description: 'Session identifier',
356
377
  isPerSession: true,
357
378
  availableFrom: { watch: false, manual: false },
379
+ swimTypes: [SwimType.INDOOR],
358
380
  },
359
381
  watchSessionId: {
360
382
  key: 'watchSessionId',
@@ -363,6 +385,7 @@ exports.METRICS_REGISTRY = {
363
385
  description: 'Watch session identifier',
364
386
  isPerSession: true,
365
387
  availableFrom: { watch: true, manual: false },
388
+ swimTypes: [SwimType.INDOOR],
366
389
  },
367
390
  date: {
368
391
  key: 'date',
@@ -371,6 +394,7 @@ exports.METRICS_REGISTRY = {
371
394
  description: 'Session date',
372
395
  isPerSession: true,
373
396
  availableFrom: { watch: true, manual: true },
397
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
374
398
  },
375
399
  source: {
376
400
  key: 'source',
@@ -379,6 +403,7 @@ exports.METRICS_REGISTRY = {
379
403
  description: 'Data source',
380
404
  isPerSession: true,
381
405
  availableFrom: { watch: false, manual: false },
406
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
382
407
  },
383
408
  lapTurnTimes: {
384
409
  key: 'lapTurnTimes',
@@ -390,6 +415,7 @@ exports.METRICS_REGISTRY = {
390
415
  isPerLap: true,
391
416
  decimals: 2,
392
417
  availableFrom: { watch: true, manual: false },
418
+ swimTypes: [SwimType.INDOOR],
393
419
  formatter: exports.formatAverageTurnTime,
394
420
  condition: (s) => !!s.lapTurnTimes?.length,
395
421
  },
@@ -401,6 +427,7 @@ exports.METRICS_REGISTRY = {
401
427
  icon: 'list-outline',
402
428
  isPerLap: true,
403
429
  availableFrom: { watch: true, manual: false },
430
+ swimTypes: [SwimType.INDOOR],
404
431
  formatter: (s) => `${s.lapStrokeTypes.length} types`,
405
432
  condition: (s) => !!s.lapStrokeTypes?.length,
406
433
  },
@@ -413,6 +440,7 @@ exports.METRICS_REGISTRY = {
413
440
  isPerLap: true,
414
441
  decimals: 1,
415
442
  availableFrom: { watch: true, manual: false },
443
+ swimTypes: [SwimType.INDOOR],
416
444
  },
417
445
  lapCalories: {
418
446
  key: 'lapCalories',
@@ -423,6 +451,7 @@ exports.METRICS_REGISTRY = {
423
451
  isPerLap: true,
424
452
  decimals: 0,
425
453
  availableFrom: { watch: true, manual: false },
454
+ swimTypes: [SwimType.INDOOR],
426
455
  },
427
456
  lapDistances: {
428
457
  key: 'lapDistances',
@@ -432,6 +461,7 @@ exports.METRICS_REGISTRY = {
432
461
  description: 'Distance per lap',
433
462
  isPerLap: true,
434
463
  availableFrom: { watch: true, manual: false },
464
+ swimTypes: [SwimType.INDOOR],
435
465
  },
436
466
  lapSwolfScores: {
437
467
  key: 'lapSwolfScores',
@@ -441,6 +471,7 @@ exports.METRICS_REGISTRY = {
441
471
  isPerLap: true,
442
472
  decimals: 0,
443
473
  availableFrom: { watch: true, manual: false },
474
+ swimTypes: [SwimType.INDOOR],
444
475
  },
445
476
  lapStrokeRates: {
446
477
  key: 'lapStrokeRates',
@@ -453,6 +484,7 @@ exports.METRICS_REGISTRY = {
453
484
  decimals: 1,
454
485
  formula: 'Lap Strokes ÷ (Lap Time in Minutes)',
455
486
  availableFrom: { watch: true, manual: false },
487
+ swimTypes: [SwimType.INDOOR],
456
488
  },
457
489
  lapDistancesPerStroke: {
458
490
  key: 'lapDistancesPerStroke',
@@ -465,6 +497,7 @@ exports.METRICS_REGISTRY = {
465
497
  decimals: 2,
466
498
  formula: 'Lap Distance ÷ Lap Strokes',
467
499
  availableFrom: { watch: true, manual: false },
500
+ swimTypes: [SwimType.INDOOR],
468
501
  },
469
502
  poolLocation: {
470
503
  key: 'poolLocation',
@@ -473,6 +506,7 @@ exports.METRICS_REGISTRY = {
473
506
  description: 'Pool location details',
474
507
  isPerSession: true,
475
508
  availableFrom: { watch: false, manual: true },
509
+ swimTypes: [SwimType.INDOOR],
476
510
  },
477
511
  notes: {
478
512
  key: 'notes',
@@ -481,6 +515,7 @@ exports.METRICS_REGISTRY = {
481
515
  description: 'Session notes',
482
516
  isPerSession: true,
483
517
  availableFrom: { watch: false, manual: true },
518
+ swimTypes: [SwimType.INDOOR],
484
519
  },
485
520
  trainingTypes: {
486
521
  key: 'trainingTypes',
@@ -492,6 +527,7 @@ exports.METRICS_REGISTRY = {
492
527
  availableFrom: { watch: false, manual: true },
493
528
  formatter: (s) => s.trainingTypes?.join(', ') || '',
494
529
  condition: (s) => !!s.trainingTypes?.length,
530
+ swimTypes: [SwimType.INDOOR],
495
531
  },
496
532
  perceivedEffort: {
497
533
  key: 'perceivedEffort',
@@ -503,6 +539,7 @@ exports.METRICS_REGISTRY = {
503
539
  availableFrom: { watch: false, manual: true },
504
540
  formatter: (s) => s.perceivedEffort,
505
541
  condition: (s) => !!s.perceivedEffort,
542
+ swimTypes: [SwimType.INDOOR],
506
543
  },
507
544
  equipment: {
508
545
  key: 'equipment',
@@ -514,6 +551,19 @@ exports.METRICS_REGISTRY = {
514
551
  availableFrom: { watch: false, manual: true },
515
552
  formatter: (s) => s.equipment?.join(', ') || '',
516
553
  condition: (s) => !!s.equipment?.length,
554
+ swimTypes: [SwimType.INDOOR, SwimType.OPEN_WATER],
555
+ },
556
+ routeCoordinates: {
557
+ key: 'routeCoordinates',
558
+ label: 'GPS Route',
559
+ category: MetricCategory.PERFORMANCE,
560
+ description: 'GPS coordinates of the swimming route',
561
+ icon: 'map-outline',
562
+ isPerSession: true,
563
+ availableFrom: { watch: true, manual: false },
564
+ swimTypes: [SwimType.OPEN_WATER],
565
+ formatter: (s) => s.routeCoordinates ? `${s.routeCoordinates.length} points` : '',
566
+ condition: (s) => !!s.routeCoordinates?.length,
517
567
  },
518
568
  };
519
569
  exports.MetricsHelper = {
@@ -521,5 +571,8 @@ exports.MetricsHelper = {
521
571
  getSessionMetrics: () => Object.values(exports.METRICS_REGISTRY).filter((m) => m.isPerSession),
522
572
  getLapMetrics: () => Object.values(exports.METRICS_REGISTRY).filter((m) => m.isPerLap),
523
573
  getMetricsByCategory: (category) => Object.values(exports.METRICS_REGISTRY).filter((m) => m.category === category),
574
+ getMetricsBySwimType: (swimType) => Object.values(exports.METRICS_REGISTRY).filter((m) => m.swimTypes?.includes(swimType)),
575
+ getIndoorMetrics: () => Object.values(exports.METRICS_REGISTRY).filter((m) => m.swimTypes?.includes(SwimType.INDOOR)),
576
+ getOpenWaterMetrics: () => Object.values(exports.METRICS_REGISTRY).filter((m) => m.swimTypes?.includes(SwimType.OPEN_WATER)),
524
577
  getAllMetricKeys: () => Object.keys(exports.METRICS_REGISTRY),
525
578
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swimedge/metrics",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Shared metrics registry for SwimEdge (frontend, backend, watch)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",