fit-file-parser 5.2.0 → 5.2.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 +10 -0
- package/README.md +7 -3
- package/dist/binary.js +5 -52
- package/dist/cjs/binary.js +5 -52
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 5.2.1
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Preserve unusual vendor field definitions in lossless raw-message output so
|
|
10
|
+
domain consumers can validate only the messages they understand.
|
|
11
|
+
- Add corpus checks for raw-message-only and combined raw/decoded parsing.
|
|
12
|
+
|
|
13
|
+
## 5.2.0
|
|
14
|
+
|
|
5
15
|
### Added
|
|
6
16
|
|
|
7
17
|
- Add opt-in, message-filterable `raw_messages` output with exact native and
|
package/README.md
CHANGED
|
@@ -388,12 +388,16 @@ aggregate-only validation command:
|
|
|
388
388
|
```sh
|
|
389
389
|
git clone https://github.com/ThomasKuehne/FIT-test-files.git ../FIT-test-files
|
|
390
390
|
npm run corpus:check -- ../FIT-test-files --allow-force-recovery
|
|
391
|
+
npm run corpus:check -- ../FIT-test-files --allow-force-recovery --raw-messages
|
|
392
|
+
npm run corpus:check -- ../FIT-test-files --allow-force-recovery --raw-messages-with-decoded-output
|
|
391
393
|
```
|
|
392
394
|
|
|
393
395
|
The command accepts any corpus path; the sibling location is only a convenient
|
|
394
|
-
convention.
|
|
395
|
-
|
|
396
|
-
|
|
396
|
+
convention. Add `--raw-messages` to exercise lossless raw-message-only parsing
|
|
397
|
+
for every message in each file, or `--raw-messages-with-decoded-output` to also
|
|
398
|
+
verify the ordinary decoded output path. The corpus contains a known header-CRC
|
|
399
|
+
failure that is expected to recover only in force mode. It reports aggregate
|
|
400
|
+
counts and never prints file names or parsed activity data.
|
|
397
401
|
|
|
398
402
|
Do not edit `src/garmin_profile.generated.ts` or `src/fit_types.ts` manually.
|
|
399
403
|
Update the pinned SDK, audited vendor extensions, or a generator, then run
|
package/dist/binary.js
CHANGED
|
@@ -8,36 +8,14 @@ const GarminTimeOffset = 631065600000;
|
|
|
8
8
|
const InvalidFieldData = Symbol('invalid FIT field data');
|
|
9
9
|
const formatTypeMetadata = new Map();
|
|
10
10
|
const uint8CompatibleTypes = new Set(['enum', 'uint8', 'byte']);
|
|
11
|
-
const fitBaseTypeWidths = new Map([
|
|
12
|
-
[0, 1],
|
|
13
|
-
[1, 1],
|
|
14
|
-
[2, 1],
|
|
15
|
-
[3, 2],
|
|
16
|
-
[4, 2],
|
|
17
|
-
[5, 4],
|
|
18
|
-
[6, 4],
|
|
19
|
-
[7, 1],
|
|
20
|
-
[8, 4],
|
|
21
|
-
[9, 8],
|
|
22
|
-
[10, 1],
|
|
23
|
-
[11, 2],
|
|
24
|
-
[12, 4],
|
|
25
|
-
[13, 1],
|
|
26
|
-
[14, 8],
|
|
27
|
-
[15, 8],
|
|
28
|
-
[16, 8],
|
|
29
|
-
]);
|
|
30
11
|
function retainsRawMessages(options) {
|
|
31
12
|
return options.includeRawMessages === true
|
|
32
13
|
|| Array.isArray(options.includeRawMessages);
|
|
33
14
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const typeId = baseType & 0x1F;
|
|
39
|
-
const width = fitBaseTypeWidths.get(typeId);
|
|
40
|
-
return width !== undefined && (typeId === 7 || size % width === 0);
|
|
15
|
+
function retainsRawMessage(options, globalMessageNumber) {
|
|
16
|
+
return options.includeRawMessages === true
|
|
17
|
+
|| (Array.isArray(options.includeRawMessages)
|
|
18
|
+
&& options.includeRawMessages.includes(globalMessageNumber));
|
|
41
19
|
}
|
|
42
20
|
function baseTypeSize(type) {
|
|
43
21
|
switch (type) {
|
|
@@ -480,18 +458,11 @@ export function readRecord(blob, messageTypes, developerFields, startIndex, opti
|
|
|
480
458
|
rawData: [],
|
|
481
459
|
};
|
|
482
460
|
const message = getFitMessage(mTypeDef.globalMessageNumber);
|
|
483
|
-
const nativeFieldNumbers = new Set();
|
|
484
461
|
for (let i = 0; i < numberOfFields; i++) {
|
|
485
462
|
const fDefIndex = startIndex + 6 + i * 3;
|
|
486
463
|
const baseType = blob[fDefIndex + 2];
|
|
487
464
|
const fieldNumber = blob[fDefIndex];
|
|
488
465
|
const fieldSize = blob[fDefIndex + 1];
|
|
489
|
-
if (retainsRawMessages(options)
|
|
490
|
-
&& (nativeFieldNumbers.has(fieldNumber)
|
|
491
|
-
|| !isValidRawFieldDefinition(fieldSize, baseType))) {
|
|
492
|
-
throw new Error('Invalid FIT native field definition');
|
|
493
|
-
}
|
|
494
|
-
nativeFieldNumbers.add(fieldNumber);
|
|
495
466
|
const wireType = FIT.types.fit_base_type[baseType];
|
|
496
467
|
const { field, type, baseType: profileBaseType, array, scale, offset, units, } = message.getAttributes(blob[fDefIndex]);
|
|
497
468
|
const profileCompatible = areProfileBaseTypesCompatible(profileBaseType, wireType);
|
|
@@ -515,15 +486,8 @@ export function readRecord(blob, messageTypes, developerFields, startIndex, opti
|
|
|
515
486
|
};
|
|
516
487
|
mTypeDef.fieldDefs.push(fDef);
|
|
517
488
|
}
|
|
518
|
-
const developerFieldNumbers = new Set();
|
|
519
489
|
for (let i = 0; i < numberOfDeveloperDataFields; i++) {
|
|
520
490
|
const fDefIndex = startIndex + 6 + numberOfFields * 3 + 1 + i * 3;
|
|
521
|
-
const developerFieldKey = `${blob[fDefIndex + 2]}:${blob[fDefIndex]}`;
|
|
522
|
-
if (retainsRawMessages(options)
|
|
523
|
-
&& (blob[fDefIndex + 1] === 0 || developerFieldNumbers.has(developerFieldKey))) {
|
|
524
|
-
throw new Error('Invalid FIT developer field definition');
|
|
525
|
-
}
|
|
526
|
-
developerFieldNumbers.add(developerFieldKey);
|
|
527
491
|
(_a = mTypeDef.developerFieldDefs) === null || _a === void 0 ? void 0 : _a.push({
|
|
528
492
|
fieldDefinitionNumber: blob[fDefIndex],
|
|
529
493
|
size: blob[fDefIndex + 1],
|
|
@@ -546,24 +510,13 @@ export function readRecord(blob, messageTypes, developerFields, startIndex, opti
|
|
|
546
510
|
if (!messageType) {
|
|
547
511
|
throw new Error('FIT data record has no local definition');
|
|
548
512
|
}
|
|
549
|
-
if (isCompressedTimestamp && retainsRawMessages(options)) {
|
|
550
|
-
const timestampField = messageType.fieldDefs[0];
|
|
551
|
-
if (!timestampField
|
|
552
|
-
|| timestampField.fDefNo !== 253
|
|
553
|
-
|| timestampField.size !== 4
|
|
554
|
-
|| (timestampField.baseTypeNo & 0x1F) !== 6) {
|
|
555
|
-
throw new Error('Invalid FIT compressed timestamp definition');
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
513
|
let messageSize = 0;
|
|
559
514
|
let readDataFromIndex = startIndex + 1;
|
|
560
515
|
const fields = {};
|
|
561
516
|
const message = getFitMessage(messageType.globalMessageNumber);
|
|
562
517
|
const developerFieldDefs = (_b = messageType.developerFieldDefs) !== null && _b !== void 0 ? _b : [];
|
|
563
518
|
const totalFieldCount = messageType.fieldDefs.length + developerFieldDefs.length;
|
|
564
|
-
const includeRawMessage = options.
|
|
565
|
-
|| (Array.isArray(options.includeRawMessages)
|
|
566
|
-
&& options.includeRawMessages.includes(messageType.globalMessageNumber));
|
|
519
|
+
const includeRawMessage = retainsRawMessage(options, messageType.globalMessageNumber);
|
|
567
520
|
const includeRawDeveloperFields = options.includeRawDeveloperFields === true
|
|
568
521
|
|| (Array.isArray(options.includeRawDeveloperFields)
|
|
569
522
|
&& options.includeRawDeveloperFields.includes(messageType.globalMessageNumber));
|
package/dist/cjs/binary.js
CHANGED
|
@@ -14,36 +14,14 @@ const GarminTimeOffset = 631065600000;
|
|
|
14
14
|
const InvalidFieldData = Symbol('invalid FIT field data');
|
|
15
15
|
const formatTypeMetadata = new Map();
|
|
16
16
|
const uint8CompatibleTypes = new Set(['enum', 'uint8', 'byte']);
|
|
17
|
-
const fitBaseTypeWidths = new Map([
|
|
18
|
-
[0, 1],
|
|
19
|
-
[1, 1],
|
|
20
|
-
[2, 1],
|
|
21
|
-
[3, 2],
|
|
22
|
-
[4, 2],
|
|
23
|
-
[5, 4],
|
|
24
|
-
[6, 4],
|
|
25
|
-
[7, 1],
|
|
26
|
-
[8, 4],
|
|
27
|
-
[9, 8],
|
|
28
|
-
[10, 1],
|
|
29
|
-
[11, 2],
|
|
30
|
-
[12, 4],
|
|
31
|
-
[13, 1],
|
|
32
|
-
[14, 8],
|
|
33
|
-
[15, 8],
|
|
34
|
-
[16, 8],
|
|
35
|
-
]);
|
|
36
17
|
function retainsRawMessages(options) {
|
|
37
18
|
return options.includeRawMessages === true
|
|
38
19
|
|| Array.isArray(options.includeRawMessages);
|
|
39
20
|
}
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const typeId = baseType & 0x1F;
|
|
45
|
-
const width = fitBaseTypeWidths.get(typeId);
|
|
46
|
-
return width !== undefined && (typeId === 7 || size % width === 0);
|
|
21
|
+
function retainsRawMessage(options, globalMessageNumber) {
|
|
22
|
+
return options.includeRawMessages === true
|
|
23
|
+
|| (Array.isArray(options.includeRawMessages)
|
|
24
|
+
&& options.includeRawMessages.includes(globalMessageNumber));
|
|
47
25
|
}
|
|
48
26
|
function baseTypeSize(type) {
|
|
49
27
|
switch (type) {
|
|
@@ -486,18 +464,11 @@ function readRecord(blob, messageTypes, developerFields, startIndex, options, st
|
|
|
486
464
|
rawData: [],
|
|
487
465
|
};
|
|
488
466
|
const message = (0, messages_js_1.getFitMessage)(mTypeDef.globalMessageNumber);
|
|
489
|
-
const nativeFieldNumbers = new Set();
|
|
490
467
|
for (let i = 0; i < numberOfFields; i++) {
|
|
491
468
|
const fDefIndex = startIndex + 6 + i * 3;
|
|
492
469
|
const baseType = blob[fDefIndex + 2];
|
|
493
470
|
const fieldNumber = blob[fDefIndex];
|
|
494
471
|
const fieldSize = blob[fDefIndex + 1];
|
|
495
|
-
if (retainsRawMessages(options)
|
|
496
|
-
&& (nativeFieldNumbers.has(fieldNumber)
|
|
497
|
-
|| !isValidRawFieldDefinition(fieldSize, baseType))) {
|
|
498
|
-
throw new Error('Invalid FIT native field definition');
|
|
499
|
-
}
|
|
500
|
-
nativeFieldNumbers.add(fieldNumber);
|
|
501
472
|
const wireType = fit_js_1.FIT.types.fit_base_type[baseType];
|
|
502
473
|
const { field, type, baseType: profileBaseType, array, scale, offset, units, } = message.getAttributes(blob[fDefIndex]);
|
|
503
474
|
const profileCompatible = areProfileBaseTypesCompatible(profileBaseType, wireType);
|
|
@@ -521,15 +492,8 @@ function readRecord(blob, messageTypes, developerFields, startIndex, options, st
|
|
|
521
492
|
};
|
|
522
493
|
mTypeDef.fieldDefs.push(fDef);
|
|
523
494
|
}
|
|
524
|
-
const developerFieldNumbers = new Set();
|
|
525
495
|
for (let i = 0; i < numberOfDeveloperDataFields; i++) {
|
|
526
496
|
const fDefIndex = startIndex + 6 + numberOfFields * 3 + 1 + i * 3;
|
|
527
|
-
const developerFieldKey = `${blob[fDefIndex + 2]}:${blob[fDefIndex]}`;
|
|
528
|
-
if (retainsRawMessages(options)
|
|
529
|
-
&& (blob[fDefIndex + 1] === 0 || developerFieldNumbers.has(developerFieldKey))) {
|
|
530
|
-
throw new Error('Invalid FIT developer field definition');
|
|
531
|
-
}
|
|
532
|
-
developerFieldNumbers.add(developerFieldKey);
|
|
533
497
|
(_a = mTypeDef.developerFieldDefs) === null || _a === void 0 ? void 0 : _a.push({
|
|
534
498
|
fieldDefinitionNumber: blob[fDefIndex],
|
|
535
499
|
size: blob[fDefIndex + 1],
|
|
@@ -552,24 +516,13 @@ function readRecord(blob, messageTypes, developerFields, startIndex, options, st
|
|
|
552
516
|
if (!messageType) {
|
|
553
517
|
throw new Error('FIT data record has no local definition');
|
|
554
518
|
}
|
|
555
|
-
if (isCompressedTimestamp && retainsRawMessages(options)) {
|
|
556
|
-
const timestampField = messageType.fieldDefs[0];
|
|
557
|
-
if (!timestampField
|
|
558
|
-
|| timestampField.fDefNo !== 253
|
|
559
|
-
|| timestampField.size !== 4
|
|
560
|
-
|| (timestampField.baseTypeNo & 0x1F) !== 6) {
|
|
561
|
-
throw new Error('Invalid FIT compressed timestamp definition');
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
519
|
let messageSize = 0;
|
|
565
520
|
let readDataFromIndex = startIndex + 1;
|
|
566
521
|
const fields = {};
|
|
567
522
|
const message = (0, messages_js_1.getFitMessage)(messageType.globalMessageNumber);
|
|
568
523
|
const developerFieldDefs = (_b = messageType.developerFieldDefs) !== null && _b !== void 0 ? _b : [];
|
|
569
524
|
const totalFieldCount = messageType.fieldDefs.length + developerFieldDefs.length;
|
|
570
|
-
const includeRawMessage = options.
|
|
571
|
-
|| (Array.isArray(options.includeRawMessages)
|
|
572
|
-
&& options.includeRawMessages.includes(messageType.globalMessageNumber));
|
|
525
|
+
const includeRawMessage = retainsRawMessage(options, messageType.globalMessageNumber);
|
|
573
526
|
const includeRawDeveloperFields = options.includeRawDeveloperFields === true
|
|
574
527
|
|| (Array.isArray(options.includeRawDeveloperFields)
|
|
575
528
|
&& options.includeRawDeveloperFields.includes(messageType.globalMessageNumber));
|