fit-file-parser 5.2.1 → 6.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.
@@ -3,7 +3,7 @@ import { mapDataIntoLap, mapDataIntoSession } from './helper.js';
3
3
  export { FitBaseType, FitEncoder } from './fit-encoder.js';
4
4
  export default class FitParser {
5
5
  constructor(options = {}) {
6
- var _a, _b, _c;
6
+ var _a, _b, _c, _d;
7
7
  this.options = {
8
8
  force: options.force != null ? options.force : true,
9
9
  speedUnit: options.speedUnit || 'm/s',
@@ -14,7 +14,8 @@ export default class FitParser {
14
14
  mode: options.mode || 'list',
15
15
  includeRawDeveloperFields: (_a = options.includeRawDeveloperFields) !== null && _a !== void 0 ? _a : false,
16
16
  includeRawMessages: (_b = options.includeRawMessages) !== null && _b !== void 0 ? _b : false,
17
- rawMessagesOnly: (_c = options.rawMessagesOnly) !== null && _c !== void 0 ? _c : false,
17
+ includeUnmappedMessages: (_c = options.includeUnmappedMessages) !== null && _c !== void 0 ? _c : false,
18
+ rawMessagesOnly: (_d = options.rawMessagesOnly) !== null && _d !== void 0 ? _d : false,
18
19
  };
19
20
  }
20
21
  parseAsync(content) {
@@ -125,6 +126,7 @@ export default class FitParser {
125
126
  || Array.isArray(this.options.includeRawMessages)
126
127
  ? []
127
128
  : undefined;
129
+ const unmappedMessages = this.options.includeUnmappedMessages ? [] : undefined;
128
130
  const messageCountsByGlobalNumber = new Map();
129
131
  let loopIndex = headerLength;
130
132
  const messageTypes = [];
@@ -136,7 +138,7 @@ export default class FitParser {
136
138
  let lastStopTimestamp;
137
139
  let pausedTime = 0;
138
140
  while (loopIndex < crcStart) {
139
- const { globalMessageNumber, littleEndian, message, messageType, nextIndex, compressedTimestamp, rawFields: recordRawFields, rawDeveloperFields: recordRawDeveloperFields, } = readRecord(blob, messageTypes, developerFields, loopIndex, this.options, startDate, pausedTime, dataView, decoderState, crcStart);
141
+ const { globalMessageNumber, littleEndian, message, messageType, nextIndex, compressedTimestamp, rawFields: recordRawFields, rawDeveloperFields: recordRawDeveloperFields, unmappedFields: recordUnmappedFields, unmappedDeveloperFields: recordUnmappedDeveloperFields, } = readRecord(blob, messageTypes, developerFields, loopIndex, this.options, startDate, pausedTime, dataView, decoderState, crcStart);
140
142
  loopIndex = nextIndex;
141
143
  if (globalMessageNumber !== undefined) {
142
144
  const messageIndex = (_a = messageCountsByGlobalNumber.get(globalMessageNumber)) !== null && _a !== void 0 ? _a : 0;
@@ -163,6 +165,20 @@ export default class FitParser {
163
165
  raw_value: field.rawValue,
164
166
  });
165
167
  });
168
+ if (littleEndian !== undefined
169
+ && ((recordUnmappedFields === null || recordUnmappedFields === void 0 ? void 0 : recordUnmappedFields.length) || (recordUnmappedDeveloperFields === null || recordUnmappedDeveloperFields === void 0 ? void 0 : recordUnmappedDeveloperFields.length))) {
170
+ unmappedMessages === null || unmappedMessages === void 0 ? void 0 : unmappedMessages.push(Object.assign(Object.assign({ global_message_number: globalMessageNumber, message_index: messageIndex, little_endian: littleEndian }, (compressedTimestamp === undefined
171
+ ? {}
172
+ : { compressed_timestamp: compressedTimestamp })), { fields: (recordUnmappedFields !== null && recordUnmappedFields !== void 0 ? recordUnmappedFields : []).map(field => ({
173
+ field_definition_number: field.fieldDefinitionNumber,
174
+ base_type: field.baseType,
175
+ raw_value: field.rawValue,
176
+ })), developer_fields: (recordUnmappedDeveloperFields !== null && recordUnmappedDeveloperFields !== void 0 ? recordUnmappedDeveloperFields : []).map(field => ({
177
+ developer_data_index: field.developerDataIndex,
178
+ field_definition_number: field.fieldDefinitionNumber,
179
+ raw_value: field.rawValue,
180
+ })) }));
181
+ }
166
182
  }
167
183
  if (this.options.rawMessagesOnly) {
168
184
  continue;
@@ -293,6 +309,9 @@ export default class FitParser {
293
309
  if (rawMessages) {
294
310
  fitObj.raw_messages = rawMessages;
295
311
  }
312
+ if (unmappedMessages && unmappedMessages.length > 0) {
313
+ fitObj.unmapped_messages = unmappedMessages;
314
+ }
296
315
  callback(undefined, fitObj);
297
316
  return;
298
317
  }
@@ -321,6 +340,9 @@ export default class FitParser {
321
340
  if (rawMessages) {
322
341
  fitObj.raw_messages = rawMessages;
323
342
  }
343
+ if (unmappedMessages && unmappedMessages.length > 0) {
344
+ fitObj.unmapped_messages = unmappedMessages;
345
+ }
324
346
  if (isCascadeNeeded) {
325
347
  laps = mapDataIntoLap(laps, 'records', records);
326
348
  laps = mapDataIntoLap(laps, 'lengths', lengths);
package/dist/fit.d.ts CHANGED
@@ -37,10 +37,4 @@ export interface FitType {
37
37
  messages: Record<number, Message>;
38
38
  types: Record<string, Record<number, string | number>>;
39
39
  }
40
- /**
41
- * Garmin fields observed in the external FIT corpus but absent from the pinned
42
- * public SDK profile. These additions may not replace standard SDK fields.
43
- */
44
- export declare const FIT_VENDOR_MESSAGE_EXTENSIONS: Record<number, Message>;
45
- export declare const FIT_VENDOR_TYPE_EXTENSIONS: Record<string, Record<number, string>>;
46
40
  export declare const FIT: FitType;
package/dist/fit.js CHANGED
@@ -1,4 +1,4 @@
1
- import { GARMIN_MESSAGES, GARMIN_TYPES } from './garmin_profile.generated.js';
1
+ import { PROFILE_MESSAGES, PROFILE_TYPES } from './profile.js';
2
2
  const metersInOneKilometer = 1000;
3
3
  const secondsInOneHour = 3600;
4
4
  // according to https://en.wikipedia.org/wiki/Mile
@@ -28,119 +28,9 @@ const options = {
28
28
  psi: { multiplier: psiInOneBar, offset: 0 },
29
29
  },
30
30
  };
31
- /**
32
- * Garmin fields observed in the external FIT corpus but absent from the pinned
33
- * public SDK profile. These additions may not replace standard SDK fields.
34
- */
35
- export const FIT_VENDOR_MESSAGE_EXTENSIONS = {
36
- 18: {
37
- name: 'session',
38
- 178: field('est_sweat_loss', 'uint16', 1, 'ml'),
39
- 188: field('primary_benefit', 'uint8'),
40
- 205: field('beginning_potential_stamina', 'uint8', 1, 'percent'),
41
- 206: field('ending_potential_stamina', 'uint8', 1, 'percent'),
42
- 207: field('min_stamina', 'uint8', 1, 'percent'),
43
- },
44
- 20: {
45
- name: 'record',
46
- 90: field('garmin_performance_condition', 'sint8'),
47
- 137: field('potential_stamina', 'uint8', 1, 'percent'),
48
- 138: field('stamina', 'uint8', 1, 'percent'),
49
- },
50
- 23: {
51
- name: 'device_info',
52
- 24: field('ant_id', 'uint32z'),
53
- },
54
- // Undocumented Garmin user metrics message observed in activity FIT files.
55
- 79: {
56
- name: 'user_metrics',
57
- 0: field('vo2_max', 'uint16', 1024 / 3.5, 'ml/kg/min'),
58
- 1: field('age', 'uint8', 1, 'years'),
59
- 2: field('height', 'uint8', 100, 'm'),
60
- 3: field('weight', 'uint16', 10, 'kg'),
61
- 4: field('gender', 'gender'),
62
- 6: field('max_heart_rate', 'uint8', 1, 'bpm'),
63
- 8: field('remaining_recovery_time', 'uint16'),
64
- 11: field('lthr', 'uint16', 1, 'bpm'),
65
- 12: field('ltpower', 'uint16', 1, 'watts'),
66
- 13: field('ltspeed', 'uint16', 1000, 'm/s'),
67
- 16: field('start_of_activity', 'date_time'),
68
- 19: field('first_vo2_max', 'uint32', 65536 / 3.5, 'ml/kg/min'),
69
- 35: field('end_of_previous_activity', 'date_time'),
70
- 253: field('timestamp', 'date_time'),
71
- },
72
- // Undocumented Garmin activity metrics message observed in activity FIT files.
73
- 140: {
74
- name: 'activity_metrics',
75
- 1: field('new_max_heart_rate', 'uint8', 1, 'bpm'),
76
- 4: field('aerobic_training_effect', 'uint8', 10),
77
- 7: field('vo2_max', 'uint32', 65536 / 3.5, 'ml/kg/min'),
78
- 9: field('recovery_time', 'uint16', 1, 'min'),
79
- 11: field('sport', 'sport'),
80
- 20: field('anaerobic_training_effect', 'uint8', 10),
81
- 29: field('first_vo2_max', 'uint32', 65536 / 3.5, 'ml/kg/min'),
82
- 41: field('primary_benefit', 'uint8'),
83
- 60: field('total_ascent', 'uint16', 1, 'm'),
84
- 61: field('total_descent', 'uint16', 1, 'm'),
85
- 62: field('avg_power', 'uint16', 1, 'watts'),
86
- 63: field('avg_heart_rate', 'uint8', 1, 'bpm'),
87
- },
88
- 312: {
89
- name: 'split',
90
- 107: field('beginning_potential_stamina', 'uint8', 1, 'percent'),
91
- 108: field('ending_potential_stamina', 'uint8', 1, 'percent'),
92
- 109: field('min_stamina', 'uint8', 1, 'percent'),
93
- },
94
- };
95
- export const FIT_VENDOR_TYPE_EXTENSIONS = {
96
- mesg_num: {
97
- 79: 'user_metrics',
98
- 140: 'activity_metrics',
99
- },
100
- };
101
- function field(name, type, scale = 1, units = '', baseType) {
102
- return Object.assign(Object.assign({ field: name, type }, (baseType ? { baseType } : {})), { scale, offset: 0, units });
103
- }
104
- function mergeVendorMessages() {
105
- const messages = Object.assign({}, GARMIN_MESSAGES);
106
- Object.entries(FIT_VENDOR_MESSAGE_EXTENSIONS).forEach(([messageIdText, extension]) => {
107
- const messageId = Number(messageIdText);
108
- const standardMessage = messages[messageId];
109
- if (!standardMessage) {
110
- messages[messageId] = extension;
111
- return;
112
- }
113
- if (standardMessage.name !== extension.name) {
114
- throw new Error(`Vendor message ${messageId} conflicts with the Garmin SDK name`);
115
- }
116
- Object.keys(extension)
117
- .filter(key => key !== 'name')
118
- .forEach((fieldId) => {
119
- if (standardMessage[Number(fieldId)]) {
120
- throw new Error(`Vendor message ${messageId}, field ${fieldId} conflicts with the Garmin SDK profile`);
121
- }
122
- });
123
- messages[messageId] = Object.assign(Object.assign({}, standardMessage), extension);
124
- });
125
- return messages;
126
- }
127
- function mergeVendorTypes() {
128
- const types = Object.fromEntries(Object.entries(GARMIN_TYPES).map(([name, values]) => [name, Object.assign({}, values)]));
129
- Object.entries(FIT_VENDOR_TYPE_EXTENSIONS).forEach(([name, extension]) => {
130
- var _a;
131
- const standardValues = (_a = types[name]) !== null && _a !== void 0 ? _a : {};
132
- Object.keys(extension).forEach((valueId) => {
133
- if (standardValues[Number(valueId)] !== undefined) {
134
- throw new Error(`Vendor type ${name}, value ${valueId} conflicts with the Garmin SDK profile`);
135
- }
136
- });
137
- types[name] = Object.assign(Object.assign({}, standardValues), extension);
138
- });
139
- return types;
140
- }
141
31
  export const FIT = {
142
32
  scConst: 180 / Math.pow(2, 31),
143
33
  options,
144
- messages: mergeVendorMessages(),
145
- types: mergeVendorTypes(),
34
+ messages: PROFILE_MESSAGES,
35
+ types: PROFILE_TYPES,
146
36
  };
@@ -2097,6 +2097,7 @@ export interface ParsedFit {
2097
2097
  messages?: ParsedMessages;
2098
2098
  raw_developer_fields?: ParsedRawDeveloperField[];
2099
2099
  raw_messages?: ParsedRawFitMessage[];
2100
+ unmapped_messages?: ParsedRawFitMessage[];
2100
2101
  file_creator: ParsedFileCreator;
2101
2102
  device_settings: ParsedDeviceSettings;
2102
2103
  dive_summary?: ParsedDiveSummary;
@@ -0,0 +1,9 @@
1
+ import type { Message } from './fit.js';
2
+ /**
3
+ * Static, community-maintained FIT interoperability profile.
4
+ *
5
+ * This table is ordinary project source maintained under the repository's MIT
6
+ * license. See PROFILE.md for the maintenance and verification policy.
7
+ */
8
+ export declare const PROFILE_MESSAGES: Record<number, Message>;
9
+ export declare const PROFILE_TYPES: Record<string, Record<number, string | number>>;
@@ -1,13 +1,10 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GARMIN_TYPES = exports.GARMIN_MESSAGES = exports.GARMIN_PROFILE_VERSION = void 0;
4
- exports.GARMIN_PROFILE_VERSION = {
5
- major: 21,
6
- minor: 208,
7
- patch: 0,
8
- type: 'Release',
9
- };
10
- exports.GARMIN_MESSAGES = {
1
+ /**
2
+ * Static, community-maintained FIT interoperability profile.
3
+ *
4
+ * This table is ordinary project source maintained under the repository's MIT
5
+ * license. See PROFILE.md for the maintenance and verification policy.
6
+ */
7
+ export const PROFILE_MESSAGES = {
11
8
  0: {
12
9
  0: {
13
10
  field: 'type',
@@ -2589,6 +2586,13 @@ exports.GARMIN_MESSAGES = {
2589
2586
  offset: 0,
2590
2587
  units: 'Breaths/min',
2591
2588
  },
2589
+ 178: {
2590
+ field: 'est_sweat_loss',
2591
+ type: 'uint16',
2592
+ scale: 1,
2593
+ offset: 0,
2594
+ units: 'ml',
2595
+ },
2592
2596
  180: {
2593
2597
  field: 'enhanced_min_respiration_rate',
2594
2598
  type: 'uint16',
@@ -2643,6 +2647,13 @@ exports.GARMIN_MESSAGES = {
2643
2647
  offset: 0,
2644
2648
  units: 'Flow',
2645
2649
  },
2650
+ 188: {
2651
+ field: 'primary_benefit',
2652
+ type: 'uint8',
2653
+ scale: 1,
2654
+ offset: 0,
2655
+ units: '',
2656
+ },
2646
2657
  192: {
2647
2658
  field: 'workout_feel',
2648
2659
  type: 'uint8',
@@ -2724,6 +2735,27 @@ exports.GARMIN_MESSAGES = {
2724
2735
  offset: 0,
2725
2736
  units: 'm',
2726
2737
  },
2738
+ 205: {
2739
+ field: 'beginning_potential_stamina',
2740
+ type: 'uint8',
2741
+ scale: 1,
2742
+ offset: 0,
2743
+ units: 'percent',
2744
+ },
2745
+ 206: {
2746
+ field: 'ending_potential_stamina',
2747
+ type: 'uint8',
2748
+ scale: 1,
2749
+ offset: 0,
2750
+ units: 'percent',
2751
+ },
2752
+ 207: {
2753
+ field: 'min_stamina',
2754
+ type: 'uint8',
2755
+ scale: 1,
2756
+ offset: 0,
2757
+ units: 'percent',
2758
+ },
2727
2759
  208: {
2728
2760
  field: 'avg_core_temperature',
2729
2761
  type: 'uint16',
@@ -4422,6 +4454,13 @@ exports.GARMIN_MESSAGES = {
4422
4454
  offset: 0,
4423
4455
  units: 'm',
4424
4456
  },
4457
+ 90: {
4458
+ field: 'garmin_performance_condition',
4459
+ type: 'sint8',
4460
+ scale: 1,
4461
+ offset: 0,
4462
+ units: '',
4463
+ },
4425
4464
  91: {
4426
4465
  field: 'absolute_pressure',
4427
4466
  type: 'uint32',
@@ -4629,6 +4668,20 @@ exports.GARMIN_MESSAGES = {
4629
4668
  offset: 0,
4630
4669
  units: 'percent',
4631
4670
  },
4671
+ 137: {
4672
+ field: 'potential_stamina',
4673
+ type: 'uint8',
4674
+ scale: 1,
4675
+ offset: 0,
4676
+ units: 'percent',
4677
+ },
4678
+ 138: {
4679
+ field: 'stamina',
4680
+ type: 'uint8',
4681
+ scale: 1,
4682
+ offset: 0,
4683
+ units: 'percent',
4684
+ },
4632
4685
  139: {
4633
4686
  field: 'core_temperature',
4634
4687
  type: 'uint16',
@@ -4959,6 +5012,13 @@ exports.GARMIN_MESSAGES = {
4959
5012
  offset: 0,
4960
5013
  units: '',
4961
5014
  },
5015
+ 24: {
5016
+ field: 'ant_id',
5017
+ type: 'uint32z',
5018
+ scale: 1,
5019
+ offset: 0,
5020
+ units: '',
5021
+ },
4962
5022
  25: {
4963
5023
  field: 'source_type',
4964
5024
  type: 'source_type',
@@ -6401,6 +6461,107 @@ exports.GARMIN_MESSAGES = {
6401
6461
  },
6402
6462
  name: 'hrv',
6403
6463
  },
6464
+ 79: {
6465
+ 0: {
6466
+ field: 'vo2_max',
6467
+ type: 'uint16',
6468
+ scale: 292.57142857142856,
6469
+ offset: 0,
6470
+ units: 'ml/kg/min',
6471
+ },
6472
+ 1: {
6473
+ field: 'age',
6474
+ type: 'uint8',
6475
+ scale: 1,
6476
+ offset: 0,
6477
+ units: 'years',
6478
+ },
6479
+ 2: {
6480
+ field: 'height',
6481
+ type: 'uint8',
6482
+ scale: 100,
6483
+ offset: 0,
6484
+ units: 'm',
6485
+ },
6486
+ 3: {
6487
+ field: 'weight',
6488
+ type: 'uint16',
6489
+ scale: 10,
6490
+ offset: 0,
6491
+ units: 'kg',
6492
+ },
6493
+ 4: {
6494
+ field: 'gender',
6495
+ type: 'gender',
6496
+ scale: 1,
6497
+ offset: 0,
6498
+ units: '',
6499
+ },
6500
+ 6: {
6501
+ field: 'max_heart_rate',
6502
+ type: 'uint8',
6503
+ scale: 1,
6504
+ offset: 0,
6505
+ units: 'bpm',
6506
+ },
6507
+ 8: {
6508
+ field: 'remaining_recovery_time',
6509
+ type: 'uint16',
6510
+ scale: 1,
6511
+ offset: 0,
6512
+ units: '',
6513
+ },
6514
+ 11: {
6515
+ field: 'lthr',
6516
+ type: 'uint16',
6517
+ scale: 1,
6518
+ offset: 0,
6519
+ units: 'bpm',
6520
+ },
6521
+ 12: {
6522
+ field: 'ltpower',
6523
+ type: 'uint16',
6524
+ scale: 1,
6525
+ offset: 0,
6526
+ units: 'watts',
6527
+ },
6528
+ 13: {
6529
+ field: 'ltspeed',
6530
+ type: 'uint16',
6531
+ scale: 1000,
6532
+ offset: 0,
6533
+ units: 'm/s',
6534
+ },
6535
+ 16: {
6536
+ field: 'start_of_activity',
6537
+ type: 'date_time',
6538
+ scale: 1,
6539
+ offset: 0,
6540
+ units: '',
6541
+ },
6542
+ 19: {
6543
+ field: 'first_vo2_max',
6544
+ type: 'uint32',
6545
+ scale: 18724.571428571428,
6546
+ offset: 0,
6547
+ units: 'ml/kg/min',
6548
+ },
6549
+ 35: {
6550
+ field: 'end_of_previous_activity',
6551
+ type: 'date_time',
6552
+ scale: 1,
6553
+ offset: 0,
6554
+ units: '',
6555
+ },
6556
+ 253: {
6557
+ field: 'timestamp',
6558
+ type: 'date_time',
6559
+ scale: 1,
6560
+ offset: 0,
6561
+ units: '',
6562
+ },
6563
+ name: 'user_metrics',
6564
+ },
6404
6565
  80: {
6405
6566
  0: {
6406
6567
  field: 'fractional_timestamp',
@@ -7256,6 +7417,93 @@ exports.GARMIN_MESSAGES = {
7256
7417
  },
7257
7418
  name: 'hr',
7258
7419
  },
7420
+ 140: {
7421
+ 1: {
7422
+ field: 'new_max_heart_rate',
7423
+ type: 'uint8',
7424
+ scale: 1,
7425
+ offset: 0,
7426
+ units: 'bpm',
7427
+ },
7428
+ 4: {
7429
+ field: 'aerobic_training_effect',
7430
+ type: 'uint8',
7431
+ scale: 10,
7432
+ offset: 0,
7433
+ units: '',
7434
+ },
7435
+ 7: {
7436
+ field: 'vo2_max',
7437
+ type: 'uint32',
7438
+ scale: 18724.571428571428,
7439
+ offset: 0,
7440
+ units: 'ml/kg/min',
7441
+ },
7442
+ 9: {
7443
+ field: 'recovery_time',
7444
+ type: 'uint16',
7445
+ scale: 1,
7446
+ offset: 0,
7447
+ units: 'min',
7448
+ },
7449
+ 11: {
7450
+ field: 'sport',
7451
+ type: 'sport',
7452
+ scale: 1,
7453
+ offset: 0,
7454
+ units: '',
7455
+ },
7456
+ 20: {
7457
+ field: 'anaerobic_training_effect',
7458
+ type: 'uint8',
7459
+ scale: 10,
7460
+ offset: 0,
7461
+ units: '',
7462
+ },
7463
+ 29: {
7464
+ field: 'first_vo2_max',
7465
+ type: 'uint32',
7466
+ scale: 18724.571428571428,
7467
+ offset: 0,
7468
+ units: 'ml/kg/min',
7469
+ },
7470
+ 41: {
7471
+ field: 'primary_benefit',
7472
+ type: 'uint8',
7473
+ scale: 1,
7474
+ offset: 0,
7475
+ units: '',
7476
+ },
7477
+ 60: {
7478
+ field: 'total_ascent',
7479
+ type: 'uint16',
7480
+ scale: 1,
7481
+ offset: 0,
7482
+ units: 'm',
7483
+ },
7484
+ 61: {
7485
+ field: 'total_descent',
7486
+ type: 'uint16',
7487
+ scale: 1,
7488
+ offset: 0,
7489
+ units: 'm',
7490
+ },
7491
+ 62: {
7492
+ field: 'avg_power',
7493
+ type: 'uint16',
7494
+ scale: 1,
7495
+ offset: 0,
7496
+ units: 'watts',
7497
+ },
7498
+ 63: {
7499
+ field: 'avg_heart_rate',
7500
+ type: 'uint8',
7501
+ scale: 1,
7502
+ offset: 0,
7503
+ units: 'bpm',
7504
+ },
7505
+ name: 'activity_metrics',
7506
+ },
7259
7507
  142: {
7260
7508
  0: {
7261
7509
  field: 'event',
@@ -11790,6 +12038,27 @@ exports.GARMIN_MESSAGES = {
11790
12038
  offset: 0,
11791
12039
  units: 's',
11792
12040
  },
12041
+ 107: {
12042
+ field: 'beginning_potential_stamina',
12043
+ type: 'uint8',
12044
+ scale: 1,
12045
+ offset: 0,
12046
+ units: 'percent',
12047
+ },
12048
+ 108: {
12049
+ field: 'ending_potential_stamina',
12050
+ type: 'uint8',
12051
+ scale: 1,
12052
+ offset: 0,
12053
+ units: 'percent',
12054
+ },
12055
+ 109: {
12056
+ field: 'min_stamina',
12057
+ type: 'uint8',
12058
+ scale: 1,
12059
+ offset: 0,
12060
+ units: 'percent',
12061
+ },
11793
12062
  110: {
11794
12063
  field: 'total_moving_time',
11795
12064
  type: 'uint32',
@@ -13035,7 +13304,7 @@ exports.GARMIN_MESSAGES = {
13035
13304
  name: 'sleep_disruption_overnight_severity',
13036
13305
  },
13037
13306
  };
13038
- exports.GARMIN_TYPES = {
13307
+ export const PROFILE_TYPES = {
13039
13308
  activity: {
13040
13309
  0: 'manual',
13041
13310
  1: 'auto_multi_sport',
@@ -15765,6 +16034,7 @@ exports.GARMIN_TYPES = {
15765
16034
  55: 'monitoring',
15766
16035
  72: 'training_file',
15767
16036
  78: 'hrv',
16037
+ 79: 'user_metrics',
15768
16038
  80: 'ant_rx',
15769
16039
  81: 'ant_tx',
15770
16040
  82: 'ant_channel_id',
@@ -15777,6 +16047,7 @@ exports.GARMIN_TYPES = {
15777
16047
  129: 'weather_alert',
15778
16048
  131: 'cadence_zone',
15779
16049
  132: 'hr',
16050
+ 140: 'activity_metrics',
15780
16051
  142: 'segment_lap',
15781
16052
  145: 'memo_glob',
15782
16053
  148: 'segment_id',
@@ -264,6 +264,7 @@ export function generateFitType() {
264
264
  generateProperty('messages', ts.factory.createTypeReferenceNode('ParsedMessages')),
265
265
  generateArrayProperty('raw_developer_fields', ts.factory.createTypeReferenceNode('ParsedRawDeveloperField')),
266
266
  generateArrayProperty('raw_messages', ts.factory.createTypeReferenceNode('ParsedRawFitMessage')),
267
+ generateArrayProperty('unmapped_messages', ts.factory.createTypeReferenceNode('ParsedRawFitMessage')),
267
268
  ...Object.keys(referenceProperties).map(prop => generateProperty(prop, ts.factory.createTypeReferenceNode(snakeToCamel(referenceProperties[prop].replace('?', ''))), referenceProperties[prop].startsWith('?'))),
268
269
  ...Object.keys(collectionProperties).map(prop => generateArrayProperty(prop, collectionProperties[prop] === 'unknown'
269
270
  ? ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fit-file-parser",
3
3
  "type": "module",
4
- "version": "5.2.1",
4
+ "version": "6.0.2",
5
5
  "private": false,
6
6
  "description": "Parse your .FIT files easily, directly from JS (Garmin, Polar, Suunto)",
7
7
  "author": {
@@ -44,6 +44,7 @@
44
44
  "CHANGELOG.md",
45
45
  "CONTRIBUTORS.md",
46
46
  "LICENSE",
47
+ "PROFILE.md",
47
48
  "README.md",
48
49
  "dist/"
49
50
  ],
@@ -54,13 +55,13 @@
54
55
  }
55
56
  ],
56
57
  "scripts": {
57
- "dev": "tsx --watch --watch-path=src/type_generator.ts --watch-path=src/fit.ts --watch-path=src/garmin_profile.generated.ts codegen/codegen.ts",
58
- "codegen:profile": "tsx codegen/garmin-profile.ts",
59
- "codegen": "npm run codegen:profile && tsx codegen/codegen.ts",
60
- "codegen:check": "tsx codegen/garmin-profile.ts --check && tsx codegen/codegen.ts --check",
61
- "profile:audit": "npm run codegen:check && tsx scripts/profile-audit.ts",
58
+ "dev": "tsx --watch --watch-path=src/type_generator.ts --watch-path=src/fit.ts --watch-path=src/profile.ts codegen/codegen.ts",
59
+ "codegen": "tsx codegen/codegen.ts",
60
+ "codegen:check": "tsx codegen/codegen.ts --check",
61
+ "profile:check": "npm run codegen:check && tsx scripts/profile-check.ts",
62
+ "compatibility:check": "npm run build && tsx scripts/compatibility-check.ts",
62
63
  "corpus:check": "tsx scripts/corpus-check.ts",
63
- "check": "npm run profile:audit && npm run lint && npm run type-check && npm test -- --run && npm run build",
64
+ "check": "npm run profile:check && npm run lint && npm run type-check && npm test -- --run && npm run build",
64
65
  "example-output": "node examples/parse.js examples/example.fit > examples/output-example.json",
65
66
  "example-output-tank": "node examples/parse.js examples/example-diving.fit > examples/output-diving.json",
66
67
  "example-output-summary-first": "node examples/parse.js examples/triathlon_summary_first.fit > examples/output_summary_first.json",
@@ -71,7 +72,8 @@
71
72
  "lint": "eslint .",
72
73
  "fmt": "eslint --fix .",
73
74
  "type-check": "tsc --noEmit",
74
- "build": "tsc --noCheck && tsc --project tsconfig.cjs.json --noCheck && echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json",
75
+ "clean": "tsx scripts/clean-dist.ts",
76
+ "build": "npm run clean && tsc --noCheck && tsc --project tsconfig.cjs.json --noCheck && echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json",
75
77
  "prepack": "npm run build"
76
78
  },
77
79
  "test": "vitest",
@@ -80,7 +82,6 @@
80
82
  },
81
83
  "devDependencies": {
82
84
  "@antfu/eslint-config": "^6.2.0",
83
- "@garmin/fitsdk": "21.208.0",
84
85
  "@types/node": "^24.10.1",
85
86
  "@vitest/coverage-v8": "^4.0.13",
86
87
  "eslint": "^9.10.0",
@@ -1,9 +0,0 @@
1
- import type { Message } from './fit.js';
2
- export declare const GARMIN_PROFILE_VERSION: {
3
- readonly major: 21;
4
- readonly minor: 208;
5
- readonly patch: 0;
6
- readonly type: "Release";
7
- };
8
- export declare const GARMIN_MESSAGES: Record<number, Message>;
9
- export declare const GARMIN_TYPES: Record<string, Record<number, string | number>>;