@sports-alliance/sports-lib 6.1.0 → 6.1.3

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 (34) hide show
  1. package/jest.config.js +3 -2
  2. package/lib/cjs/activities/activity.d.ts +1 -0
  3. package/lib/cjs/activities/activity.interface.d.ts +5 -0
  4. package/lib/cjs/activities/activity.js +7 -0
  5. package/lib/cjs/activities/activity.types.d.ts +1 -0
  6. package/lib/cjs/activities/activity.types.js +1 -0
  7. package/lib/cjs/data/data.store.export.spec.js +2 -2
  8. package/lib/cjs/events/adapters/importers/fit/importer.fit.garmin.profile.data.d.ts +12 -0
  9. package/lib/cjs/events/adapters/importers/fit/importer.fit.garmin.profile.data.js +868 -0
  10. package/lib/cjs/events/adapters/importers/fit/importer.fit.garmin.profile.mapper.d.ts +22 -0
  11. package/lib/cjs/events/adapters/importers/fit/importer.fit.garmin.profile.mapper.js +110 -0
  12. package/lib/cjs/events/adapters/importers/fit/importer.fit.js +26 -10
  13. package/lib/cjs/events/adapters/importers/fit/importer.fit.spec.js +1 -1
  14. package/lib/cjs/index.d.ts +114 -0
  15. package/lib/cjs/index.js +124 -0
  16. package/lib/cjs/specs/activity-duration-stream.integration.spec.d.ts +1 -0
  17. package/lib/cjs/specs/activity-duration-stream.integration.spec.js +59 -0
  18. package/lib/esm/activities/activity.d.ts +1 -0
  19. package/lib/esm/activities/activity.interface.d.ts +5 -0
  20. package/lib/esm/activities/activity.js +7 -0
  21. package/lib/esm/activities/activity.types.d.ts +1 -0
  22. package/lib/esm/activities/activity.types.js +1 -0
  23. package/lib/esm/data/data.store.export.spec.js +2 -2
  24. package/lib/esm/events/adapters/importers/fit/importer.fit.garmin.profile.data.d.ts +12 -0
  25. package/lib/esm/events/adapters/importers/fit/importer.fit.garmin.profile.data.js +865 -0
  26. package/lib/esm/events/adapters/importers/fit/importer.fit.garmin.profile.mapper.d.ts +22 -0
  27. package/lib/esm/events/adapters/importers/fit/importer.fit.garmin.profile.mapper.js +106 -0
  28. package/lib/esm/events/adapters/importers/fit/importer.fit.js +26 -10
  29. package/lib/esm/events/adapters/importers/fit/importer.fit.spec.js +1 -1
  30. package/lib/esm/index.d.ts +114 -0
  31. package/lib/esm/index.js +114 -0
  32. package/lib/esm/specs/activity-duration-stream.integration.spec.d.ts +1 -0
  33. package/lib/esm/specs/activity-duration-stream.integration.spec.js +57 -0
  34. package/package.json +2 -1
@@ -0,0 +1,22 @@
1
+ export declare class GarminProfileMapper {
2
+ /**
3
+ * Translates a Garmin Product ID to a readable device name
4
+ */
5
+ static getDeviceName(productId: number | string): string | null;
6
+ /**
7
+ * Translates a Manufacturer ID to a readable name
8
+ */
9
+ static getManufacturerName(manufacturerId: number | string): string | null;
10
+ /**
11
+ * Translates a Sport ID to a readable name
12
+ */
13
+ static getSportName(sportId: number | string): string | null;
14
+ /**
15
+ * Translates a Sub-Sport ID to a readable name
16
+ */
17
+ static getSubSportName(subSportId: number | string): string | null;
18
+ /**
19
+ * Formats internal Garmin names into pretty names (e.g. fr945 -> Forerunner 945)
20
+ */
21
+ private static formatDeviceName;
22
+ }
@@ -0,0 +1,106 @@
1
+ import { GarminManufacturers, GarminProducts, GarminSports, GarminSubSports } from './importer.fit.garmin.profile.data';
2
+ export class GarminProfileMapper {
3
+ /**
4
+ * Translates a Garmin Product ID to a readable device name
5
+ */
6
+ static getDeviceName(productId) {
7
+ if (productId === null || productId === undefined) {
8
+ return null;
9
+ }
10
+ const id = typeof productId === 'string' ? parseInt(productId, 10) : productId;
11
+ const name = GarminProducts[id];
12
+ return name ? this.formatDeviceName(name) : null;
13
+ }
14
+ /**
15
+ * Translates a Manufacturer ID to a readable name
16
+ */
17
+ static getManufacturerName(manufacturerId) {
18
+ if (manufacturerId === null || manufacturerId === undefined) {
19
+ return null;
20
+ }
21
+ const id = typeof manufacturerId === 'string' ? parseInt(manufacturerId, 10) : manufacturerId;
22
+ return GarminManufacturers[id] || null;
23
+ }
24
+ /**
25
+ * Translates a Sport ID to a readable name
26
+ */
27
+ static getSportName(sportId) {
28
+ if (sportId === null || sportId === undefined) {
29
+ return null;
30
+ }
31
+ const id = typeof sportId === 'string' ? parseInt(sportId, 10) : sportId;
32
+ return GarminSports[id] || null;
33
+ }
34
+ /**
35
+ * Translates a Sub-Sport ID to a readable name
36
+ */
37
+ static getSubSportName(subSportId) {
38
+ if (subSportId === null || subSportId === undefined) {
39
+ return null;
40
+ }
41
+ const id = typeof subSportId === 'string' ? parseInt(subSportId, 10) : subSportId;
42
+ return GarminSubSports[id] || null;
43
+ }
44
+ /**
45
+ * Formats internal Garmin names into pretty names (e.g. fr945 -> Forerunner 945)
46
+ */
47
+ static formatDeviceName(name) {
48
+ if (!name)
49
+ return 'Unknown';
50
+ // First expand known abbreviations and add spaces
51
+ let formatted = name
52
+ .replace(/^fr(\d+)/i, 'Forerunner $1')
53
+ .replace(/^fenix(\d+)/i, 'Fenix $1')
54
+ .replace(/^edge(\d+)/i, 'Edge $1')
55
+ .replace(/^vivoactive/i, 'VivoActive')
56
+ .replace(/^vivosmart/i, 'VivoSmart')
57
+ .replace(/^vivofit/i, 'VivoFit')
58
+ .replace(/^vivomove/i, 'VivoMove')
59
+ .replace(/^vivosport/i, 'VivoSport')
60
+ .replace(/^approach([A-Z\d])/i, 'Approach $1')
61
+ .replace(/^marq([A-Z])/i, 'Marq $1')
62
+ .replace(/^hrm/i, 'HRM ')
63
+ .replace(/_/g, ' ') // Replace underscores with spaces
64
+ .replace(/([a-z])([A-Z0-9])/g, '$1 $2') // Add spaces between camelCase
65
+ .replace(/([0-9])([a-zA-Z])/g, '$1 $2'); // Add spaces between numbers and letters
66
+ // Capitalize and clean up special terms
67
+ formatted = formatted
68
+ .split(' ')
69
+ .map(word => {
70
+ const lower = word.toLowerCase();
71
+ if (lower === 'apac')
72
+ return 'APAC';
73
+ if (lower === 'xt')
74
+ return 'XT';
75
+ if (lower === 'lte')
76
+ return 'LTE';
77
+ if (lower === 'hr')
78
+ return 'HR';
79
+ if (lower === 'gps')
80
+ return 'GPS';
81
+ if (lower === 'ii')
82
+ return 'II';
83
+ if (lower === 'iii')
84
+ return 'III';
85
+ if (lower === 'm' && name.toLowerCase().includes('645m'))
86
+ return 'Music'; // Special case for Fr645m
87
+ if (lower === 'jpn')
88
+ return 'Japan';
89
+ if (lower === 'chn')
90
+ return 'China';
91
+ if (lower === 'twn')
92
+ return 'Taiwan';
93
+ if (lower === 'kor')
94
+ return 'Korea';
95
+ if (lower === 'rus')
96
+ return 'Russia';
97
+ if (lower === 'sea')
98
+ return 'SEA';
99
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
100
+ })
101
+ .join(' ')
102
+ .replace(/Vivo Active/g, 'VivoActive')
103
+ .trim();
104
+ return formatted;
105
+ }
106
+ }
@@ -15,8 +15,8 @@ import { ActivityTypes, ActivityTypesMoving } from '../../../../activities/activ
15
15
  import { DataDuration } from '../../../../data/data.duration';
16
16
  import { DataEnergy } from '../../../../data/data.energy';
17
17
  import { DataDistance } from '../../../../data/data.distance';
18
- import { ImporterFitGarminDeviceNames } from './importer.fit.garmin.device.names';
19
18
  import { ImporterFitSuuntoDeviceNames } from './importer.fit.suunto.device.names';
19
+ import { GarminProfileMapper } from './importer.fit.garmin.profile.mapper';
20
20
  import { DataPause } from '../../../../data/data.pause';
21
21
  import { DataCadenceAvg } from '../../../../data/data.cadence-avg';
22
22
  import { DataPowerAvg } from '../../../../data/data.power-avg';
@@ -538,13 +538,24 @@ export class EventImporterFIT {
538
538
  }
539
539
  }
540
540
  static getActivityTypeFromSessionObject(session) {
541
- if (session.sub_sport && session.sub_sport !== 'generic') {
542
- return (ActivityTypes[`${session.sport}_${session.sub_sport}`] ||
543
- `${session.sport}_${session.sub_sport}` ||
544
- session.sport ||
545
- ActivityTypes.unknown);
546
- }
547
- return ActivityTypes[session.sport] || session.sport || ActivityTypes.unknown;
541
+ const activityTypeKey = session.sub_sport && session.sub_sport !== 'generic' ? `${session.sport}_${session.sub_sport}` : session.sport;
542
+ let activityType = ActivityTypes[activityTypeKey];
543
+ if (!activityType || activityType === ActivityTypes.unknown) {
544
+ // Fallback to Garmin SDK mappings
545
+ const sportName = GarminProfileMapper.getSportName(session.sport);
546
+ const subSportName = session.sub_sport && session.sub_sport !== 'generic'
547
+ ? GarminProfileMapper.getSubSportName(session.sub_sport)
548
+ : null;
549
+ if (sportName || subSportName) {
550
+ // Try to find in ActivityTypes using the name from Garmin SDK
551
+ const nameKey = subSportName ? `${sportName}_${subSportName}` : sportName;
552
+ activityType =
553
+ ActivityTypes[nameKey] ||
554
+ ActivityTypes[sportName] ||
555
+ nameKey;
556
+ }
557
+ }
558
+ return activityType || session.sport || ActivityTypes.unknown;
548
559
  }
549
560
  // @todo move this to a mapper
550
561
  static getStatsFromObject(object, activity, isLap) {
@@ -841,7 +852,7 @@ export class EventImporterFIT {
841
852
  break;
842
853
  }
843
854
  case 'garmin': {
844
- recognizedName = ImporterFitGarminDeviceNames[productId];
855
+ recognizedName = GarminProfileMapper.getDeviceName(productId);
845
856
  creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, 'Garmin'), productId);
846
857
  break;
847
858
  }
@@ -902,7 +913,12 @@ export class EventImporterFIT {
902
913
  break;
903
914
  }
904
915
  default: {
905
- creator = new Creator(formatDeviceName(manufacturer, productName, null, null), productId);
916
+ // Try to find if it's a numeric Garmin mapping that was missed
917
+ const manufacturerName = typeof manufacturer === 'number' ? GarminProfileMapper.getManufacturerName(manufacturer) : manufacturer;
918
+ if (manufacturerName === 'garmin') {
919
+ recognizedName = GarminProfileMapper.getDeviceName(productId);
920
+ }
921
+ creator = new Creator(formatDeviceName(manufacturer, productName, recognizedName, manufacturerName === 'garmin' ? 'Garmin' : null), productId);
906
922
  }
907
923
  }
908
924
  creator.manufacturer = manufacturer;
@@ -46,7 +46,7 @@ describe('EventImporterFIT', () => {
46
46
  it('should recognize a known Garmin device', done => {
47
47
  const manufacturer = 'garmin';
48
48
  const expectedName = 'Garmin Edge 1000';
49
- const productId = 2274;
49
+ const productId = 1836;
50
50
  const fitDataObject = generateFitDeviceDataObject(manufacturer, productId);
51
51
  // When
52
52
  const creator = EventImporterFIT.getCreatorFromFitDataObject(fitDataObject);
@@ -33,3 +33,117 @@ export declare class SportsLib {
33
33
  */
34
34
  static importFromJSON(json: EventJSONInterface): EventInterface;
35
35
  }
36
+ export * from './activities/activity.interface';
37
+ export * from './activities/activity.json.interface';
38
+ export * from './activities/activity.types';
39
+ export * from './constants/constants';
40
+ export * from './data/data-aerobic-training-effect';
41
+ export * from './data/data.absolute-pressure';
42
+ export * from './data/data.accumulated-power';
43
+ export * from './data/data.activity-types';
44
+ export * from './data/data.air-power';
45
+ export * from './data/data.altitude';
46
+ export * from './data/data.altitude-avg';
47
+ export * from './data/data.altitude-gps';
48
+ export * from './data/data.altitude-max';
49
+ export * from './data/data.altitude-min';
50
+ export * from './data/data.ascent';
51
+ export * from './data/data.cadence-avg';
52
+ export * from './data/data.cadence-max';
53
+ export * from './data/data.cadence-min';
54
+ export * from './data/data.descent';
55
+ export * from './data/data.description';
56
+ export * from './data/data.device-names';
57
+ export * from './data/data.distance';
58
+ export * from './data/data.duration';
59
+ export * from './data/data.ehpe';
60
+ export * from './data/data.energy';
61
+ export * from './data/data.epoc';
62
+ export * from './data/data.evpe';
63
+ export * from './data/data.feeling';
64
+ export * from './data/data.grade-adjusted-pace';
65
+ export * from './data/data.grade-adjusted-pace-avg';
66
+ export * from './data/data.grade-adjusted-speed';
67
+ export * from './data/data.grade-adjusted-speed-avg';
68
+ export * from './data/data.heart-rate';
69
+ export * from './data/data.heart-rate-avg';
70
+ export * from './data/data.heart-rate-max';
71
+ export * from './data/data.heart-rate-min';
72
+ export * from './data/data.ibi';
73
+ export * from './data/data.interface';
74
+ export * from './data/data.latitude-degrees';
75
+ export * from './data/data.left-balance';
76
+ export * from './data/data.longitude-degrees';
77
+ export * from './data/data.moving-time';
78
+ export * from './data/data.pace';
79
+ export * from './data/data.pace-avg';
80
+ export * from './data/data.peak-epoc';
81
+ export * from './data/data.peak-training-effect';
82
+ export * from './data/data.position.interface';
83
+ export * from './data/data.power';
84
+ export * from './data/data.power-avg';
85
+ export * from './data/data.power-left';
86
+ export * from './data/data.power-max';
87
+ export * from './data/data.power-min';
88
+ export * from './data/data.power-right';
89
+ export * from './data/data.recovery-time';
90
+ export * from './data/data.right-balance';
91
+ export * from './data/data.rpe';
92
+ export * from './data/data.sea-level-pressure';
93
+ export * from './data/data.speed';
94
+ export * from './data/data.speed-avg';
95
+ export * from './data/data.speed-max';
96
+ export * from './data/data.speed-min';
97
+ export * from './data/data.start-position';
98
+ export * from './data/data.store';
99
+ export * from './data/data.stryd-altitude';
100
+ export * from './data/data.stryd-distance';
101
+ export * from './data/data.stryd-speed';
102
+ export * from './data/data.swim-pace';
103
+ export * from './data/data.swim-pace-avg';
104
+ export * from './data/data.swim-pace-max';
105
+ export * from './data/data.temperature';
106
+ export * from './data/data.temperature-avg';
107
+ export * from './data/data.temperature-max';
108
+ export * from './data/data.temperature-min';
109
+ export * from './data/data.vertical-speed';
110
+ export * from './data/data.vertical-speed-avg';
111
+ export * from './data/data.vo2-max';
112
+ export * from './data/ibi/data.ibi';
113
+ export * from './events/adapters/exporters/exporter.gpx';
114
+ export * from './events/adapters/exporters/exporter.json';
115
+ export * from './events/adapters/importers/fit/importer.fit';
116
+ export * from './events/adapters/importers/gpx/importer.gpx';
117
+ export * from './events/adapters/importers/json/importer.json';
118
+ export * from './events/adapters/importers/suunto/importer.suunto.json';
119
+ export * from './events/adapters/importers/suunto/importer.suunto.sml';
120
+ export * from './events/adapters/importers/tcx/importer.tcx';
121
+ export * from './events/event.interface';
122
+ export * from './events/event.json.interface';
123
+ export * from './events/utilities/activity.utilities';
124
+ export * from './events/utilities/event.utilities';
125
+ export * from './events/utilities/helpers';
126
+ export * from './geodesy/adapters/geolib.adapter';
127
+ export * from './laps/lap.interface';
128
+ export * from './laps/lap.types';
129
+ export * from './meta-data/event-meta-data.interface';
130
+ export * from './meta-data/meta-data';
131
+ export * from './privacy/privacy.class.interface';
132
+ export * from './service-tokens/oauth1-service-token.interface';
133
+ export * from './service-tokens/oauth2-service-token.interface';
134
+ export * from './stats/stats.class.interface';
135
+ export * from './streams/compressed.stream.interface';
136
+ export * from './streams/stream';
137
+ export * from './streams/stream.interface';
138
+ export * from './tiles/tile.settings.interface';
139
+ export * from './users/settings/dashboard/user.dashboard.settings.interface';
140
+ export * from './users/settings/user.app.settings.interface';
141
+ export * from './users/settings/user.chart.settings.interface';
142
+ export * from './users/settings/user.map.settings.interface';
143
+ export * from './users/settings/user.my-tracks.settings.interface';
144
+ export * from './users/settings/user.settings.interface';
145
+ export * from './users/settings/user.summaries.settings.interface';
146
+ export * from './users/settings/user.unit.settings.interface';
147
+ export * from './users/user';
148
+ export * from './users/user.export-to-csv.settings.interface';
149
+ export * from './users/user.service.meta.interface';
package/lib/esm/index.js CHANGED
@@ -45,3 +45,117 @@ export class SportsLib {
45
45
  return EventImporterJSON.getEventFromJSON(json);
46
46
  }
47
47
  }
48
+ export * from './activities/activity.interface';
49
+ export * from './activities/activity.json.interface';
50
+ export * from './activities/activity.types';
51
+ export * from './constants/constants';
52
+ export * from './data/data-aerobic-training-effect';
53
+ export * from './data/data.absolute-pressure';
54
+ export * from './data/data.accumulated-power';
55
+ export * from './data/data.activity-types';
56
+ export * from './data/data.air-power';
57
+ export * from './data/data.altitude';
58
+ export * from './data/data.altitude-avg';
59
+ export * from './data/data.altitude-gps';
60
+ export * from './data/data.altitude-max';
61
+ export * from './data/data.altitude-min';
62
+ export * from './data/data.ascent';
63
+ export * from './data/data.cadence-avg';
64
+ export * from './data/data.cadence-max';
65
+ export * from './data/data.cadence-min';
66
+ export * from './data/data.descent';
67
+ export * from './data/data.description';
68
+ export * from './data/data.device-names';
69
+ export * from './data/data.distance';
70
+ export * from './data/data.duration';
71
+ export * from './data/data.ehpe';
72
+ export * from './data/data.energy';
73
+ export * from './data/data.epoc';
74
+ export * from './data/data.evpe';
75
+ export * from './data/data.feeling';
76
+ export * from './data/data.grade-adjusted-pace';
77
+ export * from './data/data.grade-adjusted-pace-avg';
78
+ export * from './data/data.grade-adjusted-speed';
79
+ export * from './data/data.grade-adjusted-speed-avg';
80
+ export * from './data/data.heart-rate';
81
+ export * from './data/data.heart-rate-avg';
82
+ export * from './data/data.heart-rate-max';
83
+ export * from './data/data.heart-rate-min';
84
+ export * from './data/data.ibi';
85
+ export * from './data/data.interface';
86
+ export * from './data/data.latitude-degrees';
87
+ export * from './data/data.left-balance';
88
+ export * from './data/data.longitude-degrees';
89
+ export * from './data/data.moving-time';
90
+ export * from './data/data.pace';
91
+ export * from './data/data.pace-avg';
92
+ export * from './data/data.peak-epoc';
93
+ export * from './data/data.peak-training-effect';
94
+ export * from './data/data.position.interface';
95
+ export * from './data/data.power';
96
+ export * from './data/data.power-avg';
97
+ export * from './data/data.power-left';
98
+ export * from './data/data.power-max';
99
+ export * from './data/data.power-min';
100
+ export * from './data/data.power-right';
101
+ export * from './data/data.recovery-time';
102
+ export * from './data/data.right-balance';
103
+ export * from './data/data.rpe';
104
+ export * from './data/data.sea-level-pressure';
105
+ export * from './data/data.speed';
106
+ export * from './data/data.speed-avg';
107
+ export * from './data/data.speed-max';
108
+ export * from './data/data.speed-min';
109
+ export * from './data/data.start-position';
110
+ export * from './data/data.store';
111
+ export * from './data/data.stryd-altitude';
112
+ export * from './data/data.stryd-distance';
113
+ export * from './data/data.stryd-speed';
114
+ export * from './data/data.swim-pace';
115
+ export * from './data/data.swim-pace-avg';
116
+ export * from './data/data.swim-pace-max';
117
+ export * from './data/data.temperature';
118
+ export * from './data/data.temperature-avg';
119
+ export * from './data/data.temperature-max';
120
+ export * from './data/data.temperature-min';
121
+ export * from './data/data.vertical-speed';
122
+ export * from './data/data.vertical-speed-avg';
123
+ export * from './data/data.vo2-max';
124
+ export * from './data/ibi/data.ibi';
125
+ export * from './events/adapters/exporters/exporter.gpx';
126
+ export * from './events/adapters/exporters/exporter.json';
127
+ export * from './events/adapters/importers/fit/importer.fit';
128
+ export * from './events/adapters/importers/gpx/importer.gpx';
129
+ export * from './events/adapters/importers/json/importer.json';
130
+ export * from './events/adapters/importers/suunto/importer.suunto.json';
131
+ export * from './events/adapters/importers/suunto/importer.suunto.sml';
132
+ export * from './events/adapters/importers/tcx/importer.tcx';
133
+ export * from './events/event.interface';
134
+ export * from './events/event.json.interface';
135
+ export * from './events/utilities/activity.utilities';
136
+ export * from './events/utilities/event.utilities';
137
+ export * from './events/utilities/helpers';
138
+ export * from './geodesy/adapters/geolib.adapter';
139
+ export * from './laps/lap.interface';
140
+ export * from './laps/lap.types';
141
+ export * from './meta-data/event-meta-data.interface';
142
+ export * from './meta-data/meta-data';
143
+ export * from './privacy/privacy.class.interface';
144
+ export * from './service-tokens/oauth1-service-token.interface';
145
+ export * from './service-tokens/oauth2-service-token.interface';
146
+ export * from './stats/stats.class.interface';
147
+ export * from './streams/compressed.stream.interface';
148
+ export * from './streams/stream';
149
+ export * from './streams/stream.interface';
150
+ export * from './tiles/tile.settings.interface';
151
+ export * from './users/settings/dashboard/user.dashboard.settings.interface';
152
+ export * from './users/settings/user.app.settings.interface';
153
+ export * from './users/settings/user.chart.settings.interface';
154
+ export * from './users/settings/user.map.settings.interface';
155
+ export * from './users/settings/user.my-tracks.settings.interface';
156
+ export * from './users/settings/user.settings.interface';
157
+ export * from './users/settings/user.summaries.settings.interface';
158
+ export * from './users/settings/user.unit.settings.interface';
159
+ export * from './users/user';
160
+ export * from './users/user.export-to-csv.settings.interface';
161
+ export * from './users/user.service.meta.interface';
@@ -0,0 +1,57 @@
1
+ import { Activity } from '../activities/activity';
2
+ import { Creator } from '../creators/creator';
3
+ import { DataDistance } from '../data/data.distance';
4
+ import { ActivityTypes } from '../activities/activity.types';
5
+ import { DataDuration } from '../data/data.duration';
6
+ describe('Activity Duration Stream', () => {
7
+ let activity;
8
+ beforeEach(() => {
9
+ // Create a 10-second activity
10
+ const startDate = new Date('2023-01-01T10:00:00Z');
11
+ const endDate = new Date('2023-01-01T10:00:10Z');
12
+ const creator = new Creator('Test Creator');
13
+ activity = new Activity(startDate, endDate, ActivityTypes.Running, creator);
14
+ });
15
+ it('should generate a duration stream based on time stream logic', () => {
16
+ // Add a distance stream with some data
17
+ const distanceStream = activity.createStream(DataDistance.type);
18
+ // Data at 0s, 5s, 10s
19
+ distanceStream.getData()[0] = 0;
20
+ distanceStream.getData()[5] = 50;
21
+ distanceStream.getData()[10] = 100;
22
+ activity.addStream(distanceStream);
23
+ const durationStream = activity.generateDurationStream();
24
+ expect(durationStream.type).toBe(DataDuration.type);
25
+ const data = durationStream.getData();
26
+ // Assuming generateTimeStream fills data where source stream has data
27
+ expect(data[0]).toBe(0);
28
+ expect(data[5]).toBe(5);
29
+ expect(data[10]).toBe(10);
30
+ // Other indices should be null/undefined as per generateTimeStream logic?
31
+ // Let's verify what generateTimeStream does.
32
+ // It uses `getStreamDataByDuration` with filterNull=true.
33
+ // So it only populates indices where source stream has data.
34
+ expect(data[1]).toBeFalsy();
35
+ expect(data[2]).toBeFalsy();
36
+ });
37
+ it('should allow specifying stream types to base duration on', () => {
38
+ // Scenario where we want duration based on HeartRate, not Distance
39
+ // Although logic reuses generateTimeStream which filters by streamTypes if provided.
40
+ // But generateTimeStream implementation:
41
+ /*
42
+ if (streamTypes.length) {
43
+ streams = streams.filter(stream => streamTypes.indexOf(stream.type) !== -1);
44
+ }
45
+ */
46
+ // It unions all provided streams?
47
+ // "streams.forEach(stream => { ... })"
48
+ // Yes, it iterates all matching streams and fills the time stream.
49
+ // Let's create a custom stream
50
+ const customStream = activity.createStream('Custom');
51
+ customStream.getData()[2] = 123;
52
+ activity.addStream(customStream);
53
+ const durationStream = activity.generateDurationStream(['Custom']);
54
+ expect(durationStream.getData()[2]).toBe(2);
55
+ expect(durationStream.getData()[0]).toBeFalsy(); // Distance stream at 0 should be ignored if we only asked for Custom
56
+ });
57
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sports-alliance/sports-lib",
3
- "version": "6.1.0",
3
+ "version": "6.1.3",
4
4
  "description": "A Library to for importing / exporting and processing GPX, TCX, FIT and JSON files from services such as Strava, Movescount, Garmin, Polar etc",
5
5
  "keywords": [
6
6
  "gpx",
@@ -64,6 +64,7 @@
64
64
  "devDependencies": {
65
65
  "@babel/core": "^7.28.5",
66
66
  "@babel/preset-env": "^7.28.5",
67
+ "@garmin/fitsdk": "^21.178.0",
67
68
  "@types/jest": "^27.0.1",
68
69
  "@types/node": "^14.6.1",
69
70
  "@types/xmldom": "^0.1.31",