@garmin/fitsdk 21.202.0 → 21.208.0
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/README.md +11 -0
- package/package.json +24 -22
- package/src/accumulator.js +2 -2
- package/src/bit-stream.js +56 -9
- package/src/crc-calculator.js +2 -2
- package/src/decoder.js +42 -81
- package/src/encoder.js +2 -39
- package/src/fit.js +5 -2
- package/src/index.d.ts +22 -0
- package/src/index.js +2 -2
- package/src/mesg-definition.js +2 -2
- package/src/output-stream.js +2 -2
- package/src/profile.js +9 -5
- package/src/stream.js +107 -90
- package/src/types/crc-calculator.d.ts +34 -0
- package/src/types/decoder.d.ts +140 -0
- package/src/types/encoder.d.ts +69 -0
- package/src/types/mesg.d.ts +35 -0
- package/src/types/mesgs.d.ts +2638 -0
- package/src/types/profile.d.ts +96 -0
- package/src/types/stream.d.ts +118 -0
- package/src/types/types.d.ts +4871 -0
- package/src/types/utils.d.ts +55 -0
- package/src/utils-hr-mesg.js +2 -2
- package/src/utils-internal.js +14 -6
- package/src/utils-memo-glob.js +2 -2
- package/src/utils.js +2 -16
package/README.md
CHANGED
|
@@ -81,6 +81,7 @@ const { messages, errors } = decoder.read({
|
|
|
81
81
|
decodeMemoGlobs: false,
|
|
82
82
|
skipHeader: false,
|
|
83
83
|
dataOnly: false,
|
|
84
|
+
legacyArrayMode: false, // Deprecated
|
|
84
85
|
});
|
|
85
86
|
````
|
|
86
87
|
#### mesgListener = (messageNumber, message) => {}
|
|
@@ -193,6 +194,16 @@ When true, the decoder will read past the 14-byte header and ignore its contents
|
|
|
193
194
|
#### dataOnly: true | false
|
|
194
195
|
When true, the decoder will read the file as if the 14-byte header was not written. The decoder then assumes the file begins with a message definition and assumes file data size from the size of the file's stream.
|
|
195
196
|
|
|
197
|
+
#### legacyArrayMode: true | false *(Deprecated)*
|
|
198
|
+
legacyArrayMode is deprecated and will be removed by EOY 2026.
|
|
199
|
+
|
|
200
|
+
When false *(default)*, the decoder will adhere to the profile for array fields.
|
|
201
|
+
|
|
202
|
+
Array fields will always be returned as arrays, even when there is only one element
|
|
203
|
+
Non-array fields will always return a single value, even when there are multiple values
|
|
204
|
+
Setting legacyArrayMode option to true will revert to pre-v21.205 behavior
|
|
205
|
+
|
|
206
|
+
|
|
196
207
|
## Creating Streams
|
|
197
208
|
Stream objects contain the binary FIT data to be decoded. Stream objects can be created from byte-arrays, ArrayBuffers, and Node.js Buffers. Internally the Stream class uses an ArrayBuffer to manage the byte stream.
|
|
198
209
|
#### From a Byte Array
|
package/package.json
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
2
|
+
"name": "@garmin/fitsdk",
|
|
3
|
+
"version": "21.208.0",
|
|
4
|
+
"description": "FIT JavaScript SDK",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"types": "src/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "vitest run --silent=passed-only --typecheck"
|
|
10
|
+
},
|
|
11
|
+
"author": "Garmin International, Inc.",
|
|
12
|
+
"license": "SEE LICENSE IN LICENSE.txt",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/garmin/fit-javascript-sdk"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"src/"
|
|
19
|
+
],
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^22.0.0",
|
|
22
|
+
"typescript": "^5.0.0",
|
|
23
|
+
"vitest": "^4.1.5"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/accumulator.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// Transfer (FIT) Protocol License.
|
|
6
6
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
7
7
|
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
|
8
|
-
// Profile Version = 21.
|
|
9
|
-
// Tag = production/release/21.
|
|
8
|
+
// Profile Version = 21.208.0Release
|
|
9
|
+
// Tag = production/release/21.208.0-0-g5209d509
|
|
10
10
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
11
11
|
|
|
12
12
|
|
package/src/bit-stream.js
CHANGED
|
@@ -5,25 +5,28 @@
|
|
|
5
5
|
// Transfer (FIT) Protocol License.
|
|
6
6
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
7
7
|
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
|
8
|
-
// Profile Version = 21.
|
|
9
|
-
// Tag = production/release/21.
|
|
8
|
+
// Profile Version = 21.208.0Release
|
|
9
|
+
// Tag = production/release/21.208.0-0-g5209d509
|
|
10
10
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
import FIT from "./fit.js";
|
|
14
14
|
|
|
15
15
|
class BitStream {
|
|
16
|
+
#data = null;
|
|
16
17
|
#array = null;
|
|
17
18
|
#currentArrayPosition = 0;
|
|
18
19
|
#bitPerPosition = 0;
|
|
20
|
+
#isBigInt = false;
|
|
19
21
|
#currentByte = 0;
|
|
20
22
|
#currentBit = 0;
|
|
21
23
|
#bitsAvailable = 0;
|
|
22
24
|
|
|
23
25
|
constructor(data, baseType = FIT.BaseType.UINT8) {
|
|
24
|
-
this.#
|
|
26
|
+
this.#data = Array.isArray(data) ? data : [data];
|
|
25
27
|
const baseTypeSize = FIT.BaseTypeDefinitions[baseType].size;
|
|
26
28
|
this.#bitPerPosition = baseTypeSize * 8;
|
|
29
|
+
this.#isBigInt = this.#bitPerPosition > 32;
|
|
27
30
|
this.reset();
|
|
28
31
|
}
|
|
29
32
|
|
|
@@ -36,9 +39,11 @@ class BitStream {
|
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
reset() {
|
|
42
|
+
this.#array = null;
|
|
39
43
|
this.#currentArrayPosition = 0;
|
|
40
|
-
this.#bitsAvailable = this.#bitPerPosition * this.#
|
|
41
|
-
this.#
|
|
44
|
+
this.#bitsAvailable = this.#bitPerPosition * this.#data.length;
|
|
45
|
+
this.#currentByte = 0;
|
|
46
|
+
this.#currentBit = 0;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
readBit() {
|
|
@@ -46,12 +51,24 @@ class BitStream {
|
|
|
46
51
|
this.#throwError();
|
|
47
52
|
}
|
|
48
53
|
|
|
54
|
+
if (this.#array === null) {
|
|
55
|
+
this.#array = this.#data;
|
|
56
|
+
this.#currentArrayPosition = this.#data.length - (this.#bitsAvailable / this.#bitPerPosition);
|
|
57
|
+
this.#nextByte();
|
|
58
|
+
}
|
|
59
|
+
|
|
49
60
|
if (this.#currentBit >= this.#bitPerPosition) {
|
|
50
61
|
this.#nextByte();
|
|
51
62
|
}
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
|
|
64
|
+
let bit;
|
|
65
|
+
if (this.#isBigInt) {
|
|
66
|
+
bit = Number(this.#currentByte & 1n);
|
|
67
|
+
this.#currentByte >>= 1n;
|
|
68
|
+
} else {
|
|
69
|
+
bit = this.#currentByte & 0x01;
|
|
70
|
+
this.#currentByte >>= 1;
|
|
71
|
+
}
|
|
55
72
|
this.#currentBit++;
|
|
56
73
|
this.#bitsAvailable--;
|
|
57
74
|
|
|
@@ -59,10 +76,36 @@ class BitStream {
|
|
|
59
76
|
}
|
|
60
77
|
|
|
61
78
|
readBits(nBitsToRead) {
|
|
79
|
+
// Fast path: reading exactly one value's worth of bits and #array not yet initialized
|
|
80
|
+
if (nBitsToRead === this.#bitPerPosition && this.#array === null) {
|
|
81
|
+
if (!this.hasBitsAvailable) {
|
|
82
|
+
this.#throwError();
|
|
83
|
+
}
|
|
84
|
+
const index = this.#data.length - (this.#bitsAvailable / this.#bitPerPosition);
|
|
85
|
+
const value = this.#data[index];
|
|
86
|
+
this.#bitsAvailable -= nBitsToRead;
|
|
87
|
+
|
|
88
|
+
if (this.#isBigInt) {
|
|
89
|
+
return Number(BigInt.asUintN(nBitsToRead, value));
|
|
90
|
+
}
|
|
91
|
+
if (nBitsToRead < 32) {
|
|
92
|
+
return (value & ((1 << nBitsToRead) - 1)) >>> 0;
|
|
93
|
+
}
|
|
94
|
+
return value >>> 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (nBitsToRead <= 32) {
|
|
98
|
+
let value = 0;
|
|
99
|
+
for (let i = 0; i < nBitsToRead; i++) {
|
|
100
|
+
value |= this.readBit() << i;
|
|
101
|
+
}
|
|
102
|
+
return value >>> 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
62
105
|
let value = 0n;
|
|
63
106
|
|
|
64
|
-
for (let i = 0n; i < nBitsToRead; i++) {
|
|
65
|
-
value |= BigInt(this.readBit()) <<
|
|
107
|
+
for (let i = 0, shift = 0n; i < nBitsToRead; i++, shift++) {
|
|
108
|
+
value |= BigInt(this.readBit()) << shift;
|
|
66
109
|
}
|
|
67
110
|
|
|
68
111
|
return Number(value);
|
|
@@ -77,6 +120,10 @@ class BitStream {
|
|
|
77
120
|
this.#currentBit = 0;
|
|
78
121
|
}
|
|
79
122
|
|
|
123
|
+
get array() {
|
|
124
|
+
return this.#array;
|
|
125
|
+
}
|
|
126
|
+
|
|
80
127
|
#throwError(error = "") {
|
|
81
128
|
throw Error("FIT Runtime Error no bits available.");
|
|
82
129
|
}
|
package/src/crc-calculator.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// Transfer (FIT) Protocol License.
|
|
6
6
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
7
7
|
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
|
8
|
-
// Profile Version = 21.
|
|
9
|
-
// Tag = production/release/21.
|
|
8
|
+
// Profile Version = 21.208.0Release
|
|
9
|
+
// Tag = production/release/21.208.0-0-g5209d509
|
|
10
10
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
11
11
|
|
|
12
12
|
|
package/src/decoder.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// Transfer (FIT) Protocol License.
|
|
6
6
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
7
7
|
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
|
8
|
-
// Profile Version = 21.
|
|
9
|
-
// Tag = production/release/21.
|
|
8
|
+
// Profile Version = 21.208.0Release
|
|
9
|
+
// Tag = production/release/21.208.0-0-g5209d509
|
|
10
10
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
11
11
|
|
|
12
12
|
|
|
@@ -61,12 +61,8 @@ class Decoder {
|
|
|
61
61
|
#optIncludeUnknownData = false;
|
|
62
62
|
#optMergeHeartRates = true;
|
|
63
63
|
#optDecodeMemoGlobs = false;
|
|
64
|
+
#optLegacyArrayMode = false;
|
|
64
65
|
|
|
65
|
-
/**
|
|
66
|
-
* Creates a FIT File Decoder
|
|
67
|
-
* @constructor
|
|
68
|
-
* @param {Stream} stream - representing the FIT file to decode
|
|
69
|
-
*/
|
|
70
66
|
constructor(stream) {
|
|
71
67
|
if (stream == null) {
|
|
72
68
|
throw Error("FIT Runtime Error stream parameter is null or undefined");
|
|
@@ -75,12 +71,6 @@ class Decoder {
|
|
|
75
71
|
this.#stream = stream;
|
|
76
72
|
}
|
|
77
73
|
|
|
78
|
-
/**
|
|
79
|
-
* Inspects the file header to determine if the input stream is a FIT file
|
|
80
|
-
* @param {Stream} stream
|
|
81
|
-
* @returns {Boolean} True if the stream is a FIT file
|
|
82
|
-
* @static
|
|
83
|
-
*/
|
|
84
74
|
static isFIT(stream) {
|
|
85
75
|
try {
|
|
86
76
|
const fileHeaderSize = stream.peekByte();
|
|
@@ -104,18 +94,10 @@ class Decoder {
|
|
|
104
94
|
return true;
|
|
105
95
|
}
|
|
106
96
|
|
|
107
|
-
/**
|
|
108
|
-
* Inspects the file header to determine if the input stream is a FIT file
|
|
109
|
-
* @returns {Boolean} True if the stream is a FIT file
|
|
110
|
-
*/
|
|
111
97
|
isFIT() {
|
|
112
98
|
return Decoder.isFIT(this.#stream);
|
|
113
99
|
}
|
|
114
100
|
|
|
115
|
-
/**
|
|
116
|
-
* Checks that the input stream is a FIT file and verifies both the header and file CRC values
|
|
117
|
-
* @returns {Boolean} True if the stream passes the isFit() and CRC checks
|
|
118
|
-
*/
|
|
119
101
|
checkIntegrity() {
|
|
120
102
|
try {
|
|
121
103
|
if (!this.isFIT()) {
|
|
@@ -128,7 +110,7 @@ class Decoder {
|
|
|
128
110
|
return false;
|
|
129
111
|
}
|
|
130
112
|
|
|
131
|
-
const buf =
|
|
113
|
+
const buf = this.#stream.asUint8Array();
|
|
132
114
|
|
|
133
115
|
if (fileHeader.headerSize === HEADER_WITH_CRC_SIZE && fileHeader.headerCRC !== 0x0000
|
|
134
116
|
&& fileHeader.headerCRC != CrcCalculator.calculateCRC(buf, 0, 12)) {
|
|
@@ -147,48 +129,6 @@ class Decoder {
|
|
|
147
129
|
return true;
|
|
148
130
|
}
|
|
149
131
|
|
|
150
|
-
/**
|
|
151
|
-
* Message Listener Callback
|
|
152
|
-
*
|
|
153
|
-
* @callback Decoder~mesgListener
|
|
154
|
-
* @param {Number} mesgNum - Profile.MesgNum
|
|
155
|
-
* @param {Object} message - The message
|
|
156
|
-
*/
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Message Definition Listener Callback
|
|
160
|
-
*
|
|
161
|
-
* @callback Decoder~mesgDefinitionListener
|
|
162
|
-
* @param {Object} messageDefinition - The message Definition
|
|
163
|
-
*/
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Developer Field Description Listener Callback
|
|
167
|
-
*
|
|
168
|
-
* @callback Decoder~fieldDescriptionListener
|
|
169
|
-
* @param {Number} key - The key associated with this pairing of of Developer Data Id and Field Description Mesgs
|
|
170
|
-
* @param {Object} developerDataIdMesg - The Developer Data Id Mesg associated with this pairing
|
|
171
|
-
* @param {Object} fieldDescriptionMesg - The Field Description Mesg associated with this pairing
|
|
172
|
-
*/
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Read the messages from the stream.
|
|
176
|
-
* @param {Object=} [options] - Read options (optional)
|
|
177
|
-
* @param {Decoder~mesgListener} [options.mesgListener=null] - (optional, default null) mesgListener(mesgNum, message)
|
|
178
|
-
* @param {Decoder~mesgDefinitionListener} [options.mesgDefinitionListener=null] - (optional, default null) mesgDefinitionListener(mesgDefinition)
|
|
179
|
-
* @param {Decoder~fieldDescriptionListener} [options.fieldDescriptionListener=null] - (optional, default null) fieldDescriptionListener(key, developerDataIdMesg, fieldDescriptionMesg)
|
|
180
|
-
* @param {Boolean} [options.expandSubFields=true] - (optional, default true)
|
|
181
|
-
* @param {Boolean} [options.expandComponents=true] - (optional, default true)
|
|
182
|
-
* @param {Boolean} [options.applyScaleAndOffset=true] - (optional, default true)
|
|
183
|
-
* @param {Boolean} [options.convertTypesToStrings=true] - (optional, default true)
|
|
184
|
-
* @param {boolean} [options.convertDateTimesToDates=true] - (optional, default true)
|
|
185
|
-
* @param {Boolean} [options.includeUnknownData=false] - (optional, default false)
|
|
186
|
-
* @param {boolean} [options.mergeHeartRates=true] - (optional, default true)
|
|
187
|
-
* @param {boolean} [options.decodeMemoGlobs=true] - (optional, default false)
|
|
188
|
-
* @param {boolean} [options.skipHeader=false] - (optional, default false)
|
|
189
|
-
* @param {boolean} [options.dataOnly=false] - (optional, default false)
|
|
190
|
-
* @return {Object} result - {messages:Array, errors:Array}
|
|
191
|
-
*/
|
|
192
132
|
read({
|
|
193
133
|
mesgListener = null,
|
|
194
134
|
mesgDefinitionListener = null,
|
|
@@ -202,7 +142,8 @@ class Decoder {
|
|
|
202
142
|
mergeHeartRates = true,
|
|
203
143
|
decodeMemoGlobs = false,
|
|
204
144
|
skipHeader = false,
|
|
205
|
-
dataOnly = false,
|
|
145
|
+
dataOnly = false,
|
|
146
|
+
legacyArrayMode = false, } = {}) {
|
|
206
147
|
|
|
207
148
|
this.#mesgListener = mesgListener;
|
|
208
149
|
this.#mesgDefinitionListener = mesgDefinitionListener;
|
|
@@ -215,6 +156,7 @@ class Decoder {
|
|
|
215
156
|
this.#optIncludeUnknownData = includeUnknownData;
|
|
216
157
|
this.#optMergeHeartRates = mergeHeartRates;
|
|
217
158
|
this.#optDecodeMemoGlobs = decodeMemoGlobs;
|
|
159
|
+
this.#optLegacyArrayMode = legacyArrayMode;
|
|
218
160
|
|
|
219
161
|
this.#localMessageDefinitions = [];
|
|
220
162
|
this.#developerDataDefinitions = {};
|
|
@@ -224,6 +166,10 @@ class Decoder {
|
|
|
224
166
|
const errors = [];
|
|
225
167
|
|
|
226
168
|
try {
|
|
169
|
+
if (this.#optLegacyArrayMode) {
|
|
170
|
+
console.log("Decoder Warning: legacyArrayMode is deprecated and will be removed in the future. Afterwards, the default behavior will be equivalent to legacyArrayMode=false.");
|
|
171
|
+
}
|
|
172
|
+
|
|
227
173
|
if (this.#optMergeHeartRates && (!this.#optApplyScaleAndOffset || !this.#optExpandComponents)) {
|
|
228
174
|
this.#throwError("mergeHeartRates requires applyScaleAndOffset and expandComponents to be enabled");
|
|
229
175
|
}
|
|
@@ -233,6 +179,7 @@ class Decoder {
|
|
|
233
179
|
}
|
|
234
180
|
|
|
235
181
|
this.#decodeMode = skipHeader ? DecodeMode.SKIP_HEADER : dataOnly ? DecodeMode.DATA_ONLY : DecodeMode.NORMAL;
|
|
182
|
+
this.#stream.legacyArrayMode = this.#optLegacyArrayMode;
|
|
236
183
|
|
|
237
184
|
while (this.#stream.position < this.#stream.length) {
|
|
238
185
|
this.#decodeNextFile();
|
|
@@ -304,9 +251,9 @@ class Decoder {
|
|
|
304
251
|
messageDefinition["reserved"] = this.#stream.readByte();
|
|
305
252
|
|
|
306
253
|
messageDefinition["architecture"] = this.#stream.readByte();
|
|
307
|
-
messageDefinition["endianness"] = messageDefinition.architecture === 0
|
|
254
|
+
messageDefinition["endianness"] = messageDefinition.architecture === 0;
|
|
308
255
|
|
|
309
|
-
messageDefinition["globalMessageNumber"] = this.#stream.readUInt16({
|
|
256
|
+
messageDefinition["globalMessageNumber"] = this.#stream.readUInt16({ littleEndian: messageDefinition["endianness"] });
|
|
310
257
|
messageDefinition["numFields"] = this.#stream.readByte();
|
|
311
258
|
messageDefinition["fieldDefinitions"] = [];
|
|
312
259
|
messageDefinition["developerFieldDefinitions"] = [];
|
|
@@ -346,7 +293,7 @@ class Decoder {
|
|
|
346
293
|
}
|
|
347
294
|
}
|
|
348
295
|
|
|
349
|
-
this.#mesgDefinitionListener?.({...messageDefinition});
|
|
296
|
+
this.#mesgDefinitionListener?.({ ...messageDefinition });
|
|
350
297
|
|
|
351
298
|
let messageProfile = Profile.messages[messageDefinition.globalMessageNumber];
|
|
352
299
|
|
|
@@ -417,7 +364,7 @@ class Decoder {
|
|
|
417
364
|
developerFieldDefinition["invalidValue"] = FIT.BaseTypeDefinitions[developerFieldDefinition.baseType].invalid;
|
|
418
365
|
developerFieldDefinition["baseTypeSize"] = FIT.BaseTypeDefinitions[developerFieldDefinition.baseType].size;
|
|
419
366
|
|
|
420
|
-
const
|
|
367
|
+
const fieldValue = this.#readDeveloperFieldValue(messageDefinition, developerFieldDefinition, field);
|
|
421
368
|
|
|
422
369
|
if (fieldValue != null) {
|
|
423
370
|
developerFields[field.key] = fieldValue;
|
|
@@ -458,7 +405,7 @@ class Decoder {
|
|
|
458
405
|
return developerDataIdMesg.developerDataIndex === message.developerDataIndex;
|
|
459
406
|
}) ?? {};
|
|
460
407
|
|
|
461
|
-
this.#fieldDescriptionListener(message.key, {...developerDataIdMesg}, {...message});
|
|
408
|
+
this.#fieldDescriptionListener(message.key, { ...developerDataIdMesg }, { ...message });
|
|
462
409
|
}
|
|
463
410
|
}
|
|
464
411
|
}
|
|
@@ -485,10 +432,21 @@ class Decoder {
|
|
|
485
432
|
fieldDefinition.baseType,
|
|
486
433
|
fieldDefinition.size,
|
|
487
434
|
{
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
}
|
|
435
|
+
littleEndian: messageDefinition["endianness"],
|
|
436
|
+
isArray: field?.array ?? fieldDefinition.size > fieldDefinition.baseTypeSize,
|
|
437
|
+
},
|
|
491
438
|
);
|
|
439
|
+
|
|
440
|
+
return rawFieldValue;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
#readDeveloperFieldValue(messageDefinition, developerFieldDefinition, field) {
|
|
444
|
+
|
|
445
|
+
// Preserve arrays for developer fields
|
|
446
|
+
this.#stream.legacyArrayMode = true;
|
|
447
|
+
const rawFieldValue = this.#readRawFieldValue(messageDefinition, developerFieldDefinition, field);
|
|
448
|
+
this.#stream.legacyArrayMode = this.#optLegacyArrayMode;
|
|
449
|
+
|
|
492
450
|
return rawFieldValue;
|
|
493
451
|
}
|
|
494
452
|
|
|
@@ -570,8 +528,9 @@ class Decoder {
|
|
|
570
528
|
continue;
|
|
571
529
|
}
|
|
572
530
|
if (referenceField.rawFieldValue === map.value) {
|
|
573
|
-
|
|
574
|
-
|
|
531
|
+
const src = message[field.name];
|
|
532
|
+
const rawFieldValue = Array.isArray(src.rawFieldValue) ? [...src.rawFieldValue] : src.rawFieldValue;
|
|
533
|
+
message[subField.name] = { ...src, rawFieldValue, isSubField: true };
|
|
575
534
|
|
|
576
535
|
if (subField.hasComponents) {
|
|
577
536
|
this.#fieldsToExpand.push(subField.name);
|
|
@@ -625,6 +584,7 @@ class Decoder {
|
|
|
625
584
|
rawFieldValue: [],
|
|
626
585
|
fieldDefinitionNumber: targetField.num,
|
|
627
586
|
isExpandedField: true,
|
|
587
|
+
isArray: targetField?.array ?? false,
|
|
628
588
|
invalidValue,
|
|
629
589
|
};
|
|
630
590
|
}
|
|
@@ -663,8 +623,9 @@ class Decoder {
|
|
|
663
623
|
}
|
|
664
624
|
|
|
665
625
|
Object.keys(mesg).forEach((key) => {
|
|
666
|
-
|
|
667
|
-
mesg[key].
|
|
626
|
+
const field = mesg[key];
|
|
627
|
+
mesg[key].fieldValue = UtilsInternal.sanitizeValues(field.fieldValue, { legacyArrayMode: this.#optLegacyArrayMode, isArray: field.isArray });
|
|
628
|
+
mesg[key].rawFieldValue = UtilsInternal.sanitizeValues(field.rawFieldValue, { legacyArrayMode: this.#optLegacyArrayMode, isArray: field.isArray });
|
|
668
629
|
|
|
669
630
|
message[key] = mesg[key];
|
|
670
631
|
});
|
|
@@ -693,7 +654,7 @@ class Decoder {
|
|
|
693
654
|
if (field == null) {
|
|
694
655
|
fieldValue = rawFieldValue;
|
|
695
656
|
}
|
|
696
|
-
else if (FIT.
|
|
657
|
+
else if (FIT.NumericFieldTypesSet.has(field?.type ?? -1)) {
|
|
697
658
|
fieldValue = this.#applyScaleAndOffset(messageDefinition, field, rawFieldValue);
|
|
698
659
|
}
|
|
699
660
|
else if (field.type === "string") {
|
|
@@ -717,7 +678,7 @@ class Decoder {
|
|
|
717
678
|
return rawFieldValue;
|
|
718
679
|
}
|
|
719
680
|
|
|
720
|
-
if (FIT.
|
|
681
|
+
if (FIT.NumericFieldTypesSet.has(field?.type ?? -1) === false) {
|
|
721
682
|
return rawFieldValue;
|
|
722
683
|
}
|
|
723
684
|
|
|
@@ -760,7 +721,7 @@ class Decoder {
|
|
|
760
721
|
components.forEach((componentFieldNum, i) => {
|
|
761
722
|
const targetField = messageDefinition.fields[componentFieldNum];
|
|
762
723
|
|
|
763
|
-
if(targetField?.num == field.num && targetField?.isAccumulated) {
|
|
724
|
+
if (targetField?.num == field.num && targetField?.isAccumulated) {
|
|
764
725
|
value = (((value / field.scale) - field.offset) + containingField.offset[i]) * containingField.scale[i];
|
|
765
726
|
}
|
|
766
727
|
});
|
|
@@ -775,7 +736,7 @@ class Decoder {
|
|
|
775
736
|
return rawFieldValue;
|
|
776
737
|
}
|
|
777
738
|
|
|
778
|
-
if (FIT.
|
|
739
|
+
if (FIT.NumericFieldTypesSet.has(field?.type ?? -1)) {
|
|
779
740
|
return rawFieldValue;
|
|
780
741
|
}
|
|
781
742
|
|
package/src/encoder.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// Transfer (FIT) Protocol License.
|
|
6
6
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
7
7
|
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
|
8
|
-
// Profile Version = 21.
|
|
9
|
-
// Tag = production/release/21.
|
|
8
|
+
// Profile Version = 21.208.0Release
|
|
9
|
+
// Tag = production/release/21.208.0-0-g5209d509
|
|
10
10
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
11
11
|
|
|
12
12
|
|
|
@@ -20,17 +20,7 @@ import Utils from "./utils.js";
|
|
|
20
20
|
const HEADER_WITH_CRC_SIZE = 14;
|
|
21
21
|
const HEADER_WITHOUT_CRC_SIZE = 12;
|
|
22
22
|
|
|
23
|
-
/**
|
|
24
|
-
* A class for encoding FIT files.
|
|
25
|
-
* @class
|
|
26
|
-
*/
|
|
27
23
|
class Encoder {
|
|
28
|
-
/**
|
|
29
|
-
Creates a FIT File Encoder
|
|
30
|
-
* @param {Object} [options] - Encoder options (optional)
|
|
31
|
-
* @param {Object.<number,{object, object }} [options.fieldDescriptions=null] - (optional, default null) fieldDescriptions
|
|
32
|
-
* @constructor
|
|
33
|
-
*/
|
|
34
24
|
constructor({ fieldDescriptions = null, } = {}) {
|
|
35
25
|
this.#fieldDescriptions = {};
|
|
36
26
|
|
|
@@ -41,10 +31,6 @@ class Encoder {
|
|
|
41
31
|
this.#writeEmptyFileHeader();
|
|
42
32
|
}
|
|
43
33
|
|
|
44
|
-
/**
|
|
45
|
-
* Closes the encoder and returns the file data
|
|
46
|
-
* @returns {Uint8Array} A Uint8Array containing the file data
|
|
47
|
-
*/
|
|
48
34
|
close() {
|
|
49
35
|
this.#updateFileHeader();
|
|
50
36
|
this.#writeFileCrc();
|
|
@@ -52,23 +38,10 @@ class Encoder {
|
|
|
52
38
|
return this.#outputStream.uint8Array;
|
|
53
39
|
}
|
|
54
40
|
|
|
55
|
-
/**
|
|
56
|
-
* Encodes a mesg into the file.
|
|
57
|
-
* @param {Object} mesg - The message data
|
|
58
|
-
* @param {Number} mesg.mesgNum - The mesg number for this message
|
|
59
|
-
* @return {this}
|
|
60
|
-
*/
|
|
61
41
|
writeMesg(mesg) {
|
|
62
42
|
return this.onMesg(mesg.mesgNum, mesg);
|
|
63
43
|
}
|
|
64
44
|
|
|
65
|
-
/**
|
|
66
|
-
* Encodes a mesg into the file.
|
|
67
|
-
* This method can be used as a Decoder~mesgListener callback.
|
|
68
|
-
* @param {Number} mesgNum - The message number for this message
|
|
69
|
-
* @param {Object} mesg - The message data
|
|
70
|
-
* @return {this}
|
|
71
|
-
*/
|
|
72
45
|
onMesg(mesgNum, mesg) {
|
|
73
46
|
try {
|
|
74
47
|
const mesgDefinition = this.#createMesgDefinition(mesgNum, mesg);
|
|
@@ -113,16 +86,6 @@ class Encoder {
|
|
|
113
86
|
return this;
|
|
114
87
|
};
|
|
115
88
|
|
|
116
|
-
/**
|
|
117
|
-
* Adds a Developer Data Field Description and associated Developer Data Id Message to the Endoder
|
|
118
|
-
* This provides the Encoder with the context required to write Developer Fields to the output-stream.
|
|
119
|
-
* *** This method does not write the messages to the output-stream ***
|
|
120
|
-
* This method can be used as a Decoder~fieldDescriptionListener callback.
|
|
121
|
-
* @param {Number} key - The message number for this message
|
|
122
|
-
* @param {Object} developerDataIdMesg - The Developer Data Id mesg
|
|
123
|
-
* @param {Object} fieldDescriptionMesg - The Field Description mesg
|
|
124
|
-
* @return {this}
|
|
125
|
-
*/
|
|
126
89
|
addDeveloperField(key, developerDataIdMesg, fieldDescriptionMesg) {
|
|
127
90
|
if(developerDataIdMesg.developerDataIndex == null || fieldDescriptionMesg.developerDataIndex == null) {
|
|
128
91
|
throw new Error("addDeveloperField() - one or more developerDataIndex values are null.", {
|
package/src/fit.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// Transfer (FIT) Protocol License.
|
|
6
6
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
7
7
|
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
|
8
|
-
// Profile Version = 21.
|
|
9
|
-
// Tag = production/release/21.
|
|
8
|
+
// Profile Version = 21.208.0Release
|
|
9
|
+
// Tag = production/release/21.208.0-0-g5209d509
|
|
10
10
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
11
11
|
|
|
12
12
|
|
|
@@ -73,6 +73,8 @@ const NumericFieldTypes = [
|
|
|
73
73
|
"uint64z"
|
|
74
74
|
];
|
|
75
75
|
|
|
76
|
+
const NumericFieldTypesSet = new Set(NumericFieldTypes);
|
|
77
|
+
|
|
76
78
|
const FieldTypeToJsType = {
|
|
77
79
|
"enum": "number",
|
|
78
80
|
"sint8": "number",
|
|
@@ -202,6 +204,7 @@ export default {
|
|
|
202
204
|
BaseTypeMask,
|
|
203
205
|
BaseTypeDefinitions,
|
|
204
206
|
NumericFieldTypes,
|
|
207
|
+
NumericFieldTypesSet,
|
|
205
208
|
BigIntFieldTypes,
|
|
206
209
|
FloatingPointFieldTypes,
|
|
207
210
|
FieldTypeToBaseType,
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright 2026 Garmin International, Inc.
|
|
3
|
+
// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
|
|
4
|
+
// may not use this file except in compliance with the Flexible and Interoperable Data
|
|
5
|
+
// Transfer (FIT) Protocol License.
|
|
6
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
7
|
+
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
|
8
|
+
// Profile Version = 21.208.0Release
|
|
9
|
+
// Tag = production/release/21.208.0-0-g5209d509
|
|
10
|
+
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export * from './types/crc-calculator';
|
|
14
|
+
export * from './types/decoder';
|
|
15
|
+
export * from './types/encoder';
|
|
16
|
+
export * from './types/mesg';
|
|
17
|
+
export * from './types/mesgs';
|
|
18
|
+
export * from './types/profile';
|
|
19
|
+
export * from './types/stream';
|
|
20
|
+
export * from './types/types';
|
|
21
|
+
export { default as Types } from './types/types';
|
|
22
|
+
export * from './types/utils';
|
package/src/index.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// Transfer (FIT) Protocol License.
|
|
6
6
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
7
7
|
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
|
8
|
-
// Profile Version = 21.
|
|
9
|
-
// Tag = production/release/21.
|
|
8
|
+
// Profile Version = 21.208.0Release
|
|
9
|
+
// Tag = production/release/21.208.0-0-g5209d509
|
|
10
10
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
11
11
|
|
|
12
12
|
|
package/src/mesg-definition.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// Transfer (FIT) Protocol License.
|
|
6
6
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
7
7
|
// ****WARNING**** This file is auto-generated! Do NOT edit this file.
|
|
8
|
-
// Profile Version = 21.
|
|
9
|
-
// Tag = production/release/21.
|
|
8
|
+
// Profile Version = 21.208.0Release
|
|
9
|
+
// Tag = production/release/21.208.0-0-g5209d509
|
|
10
10
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
11
11
|
|
|
12
12
|
|