fit-file-parser 4.0.2 → 5.0.1
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 +91 -0
- package/README.md +273 -117
- package/dist/binary.d.ts +7 -4
- package/dist/binary.js +207 -163
- package/dist/cjs/binary.d.ts +7 -4
- package/dist/cjs/binary.js +207 -163
- package/dist/cjs/fit-parser.js +41 -21
- package/dist/cjs/fit.d.ts +15 -7
- package/dist/cjs/fit.js +137 -9078
- package/dist/cjs/fit_types.d.ts +1384 -298
- package/dist/cjs/garmin_profile.generated.d.ts +9 -0
- package/dist/cjs/garmin_profile.generated.js +17838 -0
- package/dist/cjs/helper.js +22 -12
- package/dist/cjs/messages.d.ts +2 -3
- package/dist/cjs/type_generator.d.ts +3 -0
- package/dist/cjs/type_generator.js +56 -27
- package/dist/fit-parser.js +41 -21
- package/dist/fit.d.ts +15 -7
- package/dist/fit.js +136 -9077
- package/dist/fit_types.d.ts +1384 -298
- package/dist/garmin_profile.generated.d.ts +9 -0
- package/dist/garmin_profile.generated.js +17835 -0
- package/dist/helper.js +22 -12
- package/dist/messages.d.ts +2 -3
- package/dist/type_generator.d.ts +3 -0
- package/dist/type_generator.js +55 -27
- package/package.json +9 -6
package/dist/cjs/fit-parser.js
CHANGED
|
@@ -31,23 +31,23 @@ class FitParser {
|
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
33
|
parse(content, callback) {
|
|
34
|
-
var _a;
|
|
34
|
+
var _a, _b;
|
|
35
35
|
const blob = content instanceof ArrayBuffer
|
|
36
36
|
? new Uint8Array(content)
|
|
37
37
|
: new Uint8Array(content.buffer, content.byteOffset, content.byteLength);
|
|
38
38
|
const dataView = new DataView(blob.buffer, blob.byteOffset, blob.byteLength);
|
|
39
39
|
if (blob.length < 12) {
|
|
40
40
|
callback('File to small to be a FIT file', undefined);
|
|
41
|
-
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
41
|
+
return;
|
|
44
42
|
}
|
|
45
43
|
const headerLength = blob[0];
|
|
46
44
|
if (headerLength !== 14 && headerLength !== 12) {
|
|
47
45
|
callback('Incorrect header size', undefined);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (blob.length < headerLength) {
|
|
49
|
+
callback('File to small to be a FIT file', undefined);
|
|
50
|
+
return;
|
|
51
51
|
}
|
|
52
52
|
let fileTypeString = '';
|
|
53
53
|
for (let i = 8; i < 12; i++) {
|
|
@@ -55,29 +55,33 @@ class FitParser {
|
|
|
55
55
|
}
|
|
56
56
|
if (fileTypeString !== '.FIT') {
|
|
57
57
|
callback('Missing \'.FIT\' in header', undefined);
|
|
58
|
-
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
58
|
+
return;
|
|
61
59
|
}
|
|
62
60
|
if (headerLength === 14 && !this.options.force) {
|
|
63
61
|
const crcHeader = blob[12] + (blob[13] << 8);
|
|
64
62
|
const crcHeaderCalc = (0, binary_js_1.calculateCRC)(blob, 0, 12);
|
|
65
|
-
if (crcHeader !== crcHeaderCalc) {
|
|
66
|
-
|
|
67
|
-
// TODO: fix Header CRC check
|
|
63
|
+
if (crcHeader !== 0 && crcHeader !== crcHeaderCalc) {
|
|
64
|
+
callback('Header CRC mismatch', undefined);
|
|
68
65
|
return;
|
|
69
66
|
}
|
|
70
67
|
}
|
|
71
68
|
const protocolVersion = blob[1];
|
|
72
69
|
const profileVersion = blob[2] + (blob[3] << 8);
|
|
73
|
-
const dataLength =
|
|
70
|
+
const dataLength = dataView.getUint32(4, true);
|
|
74
71
|
const crcStart = dataLength + headerLength;
|
|
72
|
+
if (crcStart > blob.length) {
|
|
73
|
+
callback('File data exceeds input length', undefined);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (!this.options.force && crcStart + 2 > blob.length) {
|
|
77
|
+
callback('File CRC missing', undefined);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
75
80
|
if (!this.options.force) {
|
|
76
81
|
const crcFile = blob[crcStart] + (blob[crcStart + 1] << 8);
|
|
77
|
-
const crcFileCalc = (0, binary_js_1.calculateCRC)(blob,
|
|
82
|
+
const crcFileCalc = (0, binary_js_1.calculateCRC)(blob, 0, crcStart);
|
|
78
83
|
if (crcFile !== crcFileCalc) {
|
|
79
|
-
|
|
80
|
-
// TODO: fix File CRC check
|
|
84
|
+
callback('File CRC mismatch', undefined);
|
|
81
85
|
return;
|
|
82
86
|
}
|
|
83
87
|
}
|
|
@@ -85,6 +89,7 @@ class FitParser {
|
|
|
85
89
|
profileVersion,
|
|
86
90
|
protocolVersion,
|
|
87
91
|
};
|
|
92
|
+
const messages = {};
|
|
88
93
|
let sessions = [];
|
|
89
94
|
let laps = [];
|
|
90
95
|
const records = [];
|
|
@@ -107,6 +112,7 @@ class FitParser {
|
|
|
107
112
|
const tank_updates = [];
|
|
108
113
|
const tank_summaries = [];
|
|
109
114
|
const jumps = [];
|
|
115
|
+
const sets = [];
|
|
110
116
|
const splits = [];
|
|
111
117
|
const split_summaries = [];
|
|
112
118
|
const time_in_zone = [];
|
|
@@ -115,14 +121,20 @@ class FitParser {
|
|
|
115
121
|
let loopIndex = headerLength;
|
|
116
122
|
const messageTypes = [];
|
|
117
123
|
const developerFields = [];
|
|
124
|
+
const decoderState = {};
|
|
118
125
|
const isModeCascade = this.options.mode === 'cascade';
|
|
119
126
|
const isCascadeNeeded = isModeCascade || this.options.mode === 'both';
|
|
120
127
|
let startDate;
|
|
121
128
|
let lastStopTimestamp;
|
|
122
129
|
let pausedTime = 0;
|
|
123
130
|
while (loopIndex < crcStart) {
|
|
124
|
-
const { nextIndex, messageType, message } = (0, binary_js_1.readRecord)(blob, messageTypes, developerFields, loopIndex, this.options, startDate, pausedTime, dataView);
|
|
131
|
+
const { nextIndex, messageType, message } = (0, binary_js_1.readRecord)(blob, messageTypes, developerFields, loopIndex, this.options, startDate, pausedTime, dataView, decoderState);
|
|
125
132
|
loopIndex = nextIndex;
|
|
133
|
+
if (messageType !== ''
|
|
134
|
+
&& messageType !== 'definition'
|
|
135
|
+
&& message !== undefined) {
|
|
136
|
+
((_a = messages[messageType]) !== null && _a !== void 0 ? _a : (messages[messageType] = [])).push(message);
|
|
137
|
+
}
|
|
126
138
|
switch (messageType) {
|
|
127
139
|
case 'lap':
|
|
128
140
|
laps.push(message);
|
|
@@ -156,8 +168,10 @@ class FitParser {
|
|
|
156
168
|
case 'record':
|
|
157
169
|
if (!startDate) {
|
|
158
170
|
startDate = message.timestamp;
|
|
159
|
-
|
|
160
|
-
|
|
171
|
+
if (this.options.elapsedRecordField) {
|
|
172
|
+
message.elapsed_time = 0;
|
|
173
|
+
message.timer_time = 0;
|
|
174
|
+
}
|
|
161
175
|
}
|
|
162
176
|
records.push(message);
|
|
163
177
|
break;
|
|
@@ -210,6 +224,9 @@ class FitParser {
|
|
|
210
224
|
case 'jump':
|
|
211
225
|
jumps.push(message);
|
|
212
226
|
break;
|
|
227
|
+
case 'set':
|
|
228
|
+
sets.push(message);
|
|
229
|
+
break;
|
|
213
230
|
case 'split':
|
|
214
231
|
splits.push(message);
|
|
215
232
|
break;
|
|
@@ -250,15 +267,17 @@ class FitParser {
|
|
|
250
267
|
fitObj.time_in_zone = time_in_zone;
|
|
251
268
|
fitObj.activity_metrics = activity_metrics;
|
|
252
269
|
fitObj.user_metrics = user_metrics;
|
|
270
|
+
fitObj.messages = messages;
|
|
253
271
|
if (isCascadeNeeded) {
|
|
254
272
|
laps = (0, helper_js_1.mapDataIntoLap)(laps, 'records', records);
|
|
255
273
|
laps = (0, helper_js_1.mapDataIntoLap)(laps, 'lengths', lengths);
|
|
256
274
|
sessions = (0, helper_js_1.mapDataIntoSession)(sessions, laps);
|
|
257
|
-
fitObj.activity = Object.assign(Object.assign({}, ((
|
|
275
|
+
fitObj.activity = Object.assign(Object.assign({}, ((_b = fitObj.activity) !== null && _b !== void 0 ? _b : {})), { // ugly but we assume the activity was parsed correctly with all other members correctly
|
|
258
276
|
sessions,
|
|
259
277
|
events,
|
|
260
278
|
hrv,
|
|
261
279
|
device_infos, developer_data_ids: applications, field_descriptions: fieldDescriptions, sports,
|
|
280
|
+
sets,
|
|
262
281
|
splits,
|
|
263
282
|
split_summaries });
|
|
264
283
|
}
|
|
@@ -272,6 +291,7 @@ class FitParser {
|
|
|
272
291
|
fitObj.developer_data_ids = applications;
|
|
273
292
|
fitObj.field_descriptions = fieldDescriptions;
|
|
274
293
|
fitObj.hrv = hrv;
|
|
294
|
+
fitObj.sets = sets;
|
|
275
295
|
}
|
|
276
296
|
callback(undefined, fitObj);
|
|
277
297
|
}
|
package/dist/cjs/fit.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { FitOptions, MesgNum } from './fit_types.js';
|
|
2
|
+
export type MessageName = Exclude<MesgNum, number | 'definition'>;
|
|
2
3
|
export interface FieldDefinition {
|
|
3
4
|
type: string | number;
|
|
5
|
+
rawType?: string | number;
|
|
4
6
|
fDefNo: number;
|
|
5
7
|
size: number;
|
|
8
|
+
array?: boolean;
|
|
6
9
|
endianAbility: boolean;
|
|
7
10
|
littleEndian: boolean;
|
|
8
11
|
baseTypeNo: number;
|
|
@@ -10,6 +13,7 @@ export interface FieldDefinition {
|
|
|
10
13
|
dataType: string;
|
|
11
14
|
scale?: number | null;
|
|
12
15
|
offset?: number;
|
|
16
|
+
units?: string;
|
|
13
17
|
requiresBoundedDataView?: boolean;
|
|
14
18
|
developerDataIndex?: number;
|
|
15
19
|
isDeveloperField?: boolean;
|
|
@@ -17,22 +21,26 @@ export interface FieldDefinition {
|
|
|
17
21
|
export interface MessageObject {
|
|
18
22
|
field: string;
|
|
19
23
|
type: string;
|
|
24
|
+
baseType?: string;
|
|
25
|
+
array?: boolean;
|
|
20
26
|
scale: number | null;
|
|
21
27
|
offset: number;
|
|
22
28
|
units: string;
|
|
23
29
|
}
|
|
24
30
|
export interface Message {
|
|
25
|
-
name:
|
|
31
|
+
name: MessageName;
|
|
26
32
|
[fieldId: number]: MessageObject;
|
|
27
33
|
}
|
|
28
34
|
export interface FitType {
|
|
29
35
|
scConst: number;
|
|
30
36
|
options: FitOptions;
|
|
31
|
-
messages:
|
|
32
|
-
|
|
33
|
-
};
|
|
34
|
-
types: {
|
|
35
|
-
[typeName: string]: Record<number, string | number>;
|
|
36
|
-
};
|
|
37
|
+
messages: Record<number, Message>;
|
|
38
|
+
types: Record<string, Record<number, string | number>>;
|
|
37
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>>;
|
|
38
46
|
export declare const FIT: FitType;
|