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.
- package/CHANGELOG.md +18 -197
- package/PROFILE.md +43 -0
- package/README.md +71 -81
- package/dist/binary.d.ts +2 -0
- package/dist/binary.js +23 -6
- package/dist/cjs/binary.d.ts +2 -0
- package/dist/cjs/binary.js +23 -6
- package/dist/cjs/fit-parser.d.ts +2 -0
- package/dist/cjs/fit-parser.js +25 -3
- package/dist/cjs/fit.d.ts +0 -6
- package/dist/cjs/fit.js +4 -114
- package/dist/cjs/fit_types.d.ts +1 -0
- package/dist/cjs/profile.d.ts +9 -0
- package/dist/{garmin_profile.generated.js → cjs/profile.js} +285 -8
- package/dist/cjs/type_generator.js +1 -0
- package/dist/fit-parser.d.ts +2 -0
- package/dist/fit-parser.js +25 -3
- package/dist/fit.d.ts +0 -6
- package/dist/fit.js +3 -113
- package/dist/fit_types.d.ts +1 -0
- package/dist/profile.d.ts +9 -0
- package/dist/{cjs/garmin_profile.generated.js → profile.js} +282 -11
- package/dist/type_generator.js +1 -0
- package/package.json +10 -9
- package/dist/cjs/garmin_profile.generated.d.ts +0 -9
- package/dist/garmin_profile.generated.d.ts +0 -9
package/dist/fit-parser.js
CHANGED
|
@@ -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
|
-
|
|
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 {
|
|
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:
|
|
145
|
-
types:
|
|
34
|
+
messages: PROFILE_MESSAGES,
|
|
35
|
+
types: PROFILE_TYPES,
|
|
146
36
|
};
|
package/dist/fit_types.d.ts
CHANGED
|
@@ -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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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',
|
package/dist/type_generator.js
CHANGED
|
@@ -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": "
|
|
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/
|
|
58
|
-
"codegen
|
|
59
|
-
"codegen": "
|
|
60
|
-
"
|
|
61
|
-
"
|
|
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:
|
|
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
|
-
"
|
|
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>>;
|