@garmin/fitsdk 21.158.0 → 21.161.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 CHANGED
@@ -68,6 +68,8 @@ The Read method accepts an optional options object that can be used to customize
68
68
  ````js
69
69
  const { messages, errors } = decoder.read({
70
70
  mesgListener: (messageNumber, message) => {},
71
+ mesgDefinitionListener: (mesgDefinition) => {},
72
+ fieldDescriptionListener: (key, developerDataIdMesg, fieldDescriptionMesg) => {},
71
73
  applyScaleAndOffset: true,
72
74
  expandSubFields: true,
73
75
  expandComponents: true,
@@ -97,6 +99,10 @@ const { messages, errors } = decoder.read({
97
99
 
98
100
  console.log(recordFields);
99
101
  ````
102
+ #### mesgDefinitionListener: (mesgDefinition) => {}
103
+ Optional callback function that can be used to inspect message defintions as they are decoded from the file.
104
+ #### fieldDescriptionListener: (key, developerDataIdMesg, fieldDescriptionMesg) => {}
105
+ Optional callback function that can be used to inspect developer field descriptions as they are decoded from the file.
100
106
  #### applyScaleAndOffset: true | false
101
107
  When true the scale and offset values as defined in the FIT Profile are applied to the raw field values.
102
108
  ````js
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@garmin/fitsdk",
3
- "version": "21.158.0",
3
+ "version": "21.161.0",
4
4
  "description": "FIT JavaScript SDK",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "build": "node .",
9
- "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
9
+ "test": "vitest run"
10
10
  },
11
11
  "author": "Garmin International, Inc.",
12
12
  "license": "SEE LICENSE IN LICENSE.txt",
@@ -18,9 +18,6 @@
18
18
  "src/"
19
19
  ],
20
20
  "devDependencies": {
21
- "jest": "^28.1.2"
22
- },
23
- "jest": {
24
- "transform": {}
21
+ "vitest": "^2.1.8"
25
22
  }
26
- }
23
+ }
@@ -1,12 +1,12 @@
1
1
  /////////////////////////////////////////////////////////////////////////////////////////////
2
- // Copyright 2024 Garmin International, Inc.
2
+ // Copyright 2025 Garmin International, Inc.
3
3
  // Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
4
4
  // may not use this file except in compliance with the Flexible and Interoperable Data
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.158.0Release
9
- // Tag = production/release/21.158.0-0-gc9428aa
8
+ // Profile Version = 21.161.0Release
9
+ // Tag = production/release/21.161.0-0-g58854c0
10
10
  /////////////////////////////////////////////////////////////////////////////////////////////
11
11
 
12
12
 
package/src/bit-stream.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /////////////////////////////////////////////////////////////////////////////////////////////
2
- // Copyright 2024 Garmin International, Inc.
2
+ // Copyright 2025 Garmin International, Inc.
3
3
  // Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
4
4
  // may not use this file except in compliance with the Flexible and Interoperable Data
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.158.0Release
9
- // Tag = production/release/21.158.0-0-gc9428aa
8
+ // Profile Version = 21.161.0Release
9
+ // Tag = production/release/21.161.0-0-g58854c0
10
10
  /////////////////////////////////////////////////////////////////////////////////////////////
11
11
 
12
12
 
@@ -1,12 +1,12 @@
1
1
  /////////////////////////////////////////////////////////////////////////////////////////////
2
- // Copyright 2024 Garmin International, Inc.
2
+ // Copyright 2025 Garmin International, Inc.
3
3
  // Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
4
4
  // may not use this file except in compliance with the Flexible and Interoperable Data
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.158.0Release
9
- // Tag = production/release/21.158.0-0-gc9428aa
8
+ // Profile Version = 21.161.0Release
9
+ // Tag = production/release/21.161.0-0-g58854c0
10
10
  /////////////////////////////////////////////////////////////////////////////////////////////
11
11
 
12
12
 
package/src/decoder.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /////////////////////////////////////////////////////////////////////////////////////////////
2
- // Copyright 2024 Garmin International, Inc.
2
+ // Copyright 2025 Garmin International, Inc.
3
3
  // Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
4
4
  // may not use this file except in compliance with the Flexible and Interoperable Data
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.158.0Release
9
- // Tag = production/release/21.158.0-0-gc9428aa
8
+ // Profile Version = 21.161.0Release
9
+ // Tag = production/release/21.161.0-0-g58854c0
10
10
  /////////////////////////////////////////////////////////////////////////////////////////////
11
11
 
12
12
 
@@ -48,6 +48,8 @@ class Decoder {
48
48
  #decodeMode = DecodeMode.NORMAL;
49
49
 
50
50
  #mesgListener = null;
51
+ #mesgDefinitionListener = null;
52
+ #fieldDescriptionListener = null;
51
53
  #optExpandSubFields = true;
52
54
  #optExpandComponents = true;
53
55
  #optApplyScaleAndOffset = true;
@@ -149,11 +151,28 @@ class Decoder {
149
151
  * @param {Object} message - The message
150
152
  */
151
153
 
154
+ /**
155
+ * Message Definition Listener Callback
156
+ *
157
+ * @callback Decoder~mesgDefinitionListener
158
+ * @param {Object} messageDefinition - The message Definition
159
+ */
160
+
161
+ /**
162
+ * Developer Field Description Listener Callback
163
+ *
164
+ * @callback Decoder~fieldDescriptionListener
165
+ * @param {Number} key - The key associated with this pairing of of Developer Data Id and Field Description Mesgs
166
+ * @param {Object} developerDataIdMesg - The Developer Data Id Mesg associated with this pairing
167
+ * @param {Object} fieldDescriptionMesg - The Field Description Mesg associated with this pairing
168
+ */
152
169
 
153
170
  /**
154
171
  * Read the messages from the stream.
155
172
  * @param {Object=} [options] - Read options (optional)
156
173
  * @param {Decoder~mesgListener} [options.mesgListener=null] - (optional, default null) mesgListener(mesgNum, message)
174
+ * @param {Decoder~mesgDefinitionListener} [options.mesgDefinitionListener=null] - (optional, default null) mesgDefinitionListener(mesgDefinition)
175
+ * @param {Decoder~fieldDescriptionListener} [options.fieldDescriptionListener=null] - (optional, default null) fieldDescriptionListener(key, developerDataIdMesg, fieldDescriptionMesg)
157
176
  * @param {Boolean} [options.expandSubFields=true] - (optional, default true)
158
177
  * @param {Boolean} [options.expandComponents=true] - (optional, default true)
159
178
  * @param {Boolean} [options.applyScaleAndOffset=true] - (optional, default true)
@@ -167,6 +186,8 @@ class Decoder {
167
186
  */
168
187
  read({
169
188
  mesgListener = null,
189
+ mesgDefinitionListener = null,
190
+ fieldDescriptionListener = null,
170
191
  expandSubFields = true,
171
192
  expandComponents = true,
172
193
  applyScaleAndOffset = true,
@@ -178,6 +199,8 @@ class Decoder {
178
199
  dataOnly = false,} = {}) {
179
200
 
180
201
  this.#mesgListener = mesgListener;
202
+ this.#mesgDefinitionListener = mesgDefinitionListener;
203
+ this.#fieldDescriptionListener = fieldDescriptionListener;
181
204
  this.#optExpandSubFields = expandSubFields
182
205
  this.#optExpandComponents = expandComponents;
183
206
  this.#optApplyScaleAndOffset = applyScaleAndOffset;
@@ -310,6 +333,8 @@ class Decoder {
310
333
  }
311
334
  }
312
335
 
336
+ this.#mesgDefinitionListener?.({...messageDefinition});
337
+
313
338
  let messageProfile = Profile.messages[messageDefinition.globalMessageNumber];
314
339
 
315
340
  if (messageProfile == null && this.#optIncludeUnknownData) {
@@ -414,6 +439,14 @@ class Decoder {
414
439
 
415
440
  this.#messages[messageDefinition.messagesKey].push(message);
416
441
  this.#mesgListener?.(messageDefinition.globalMessageNumber, message);
442
+
443
+ if (mesgNum === Profile.MesgNum.FIELD_DESCRIPTION && this.#fieldDescriptionListener != null) {
444
+ const developerDataIdMesg = this.#messages.developerDataIdMesgs?.find((developerDataIdMesg) => {
445
+ return developerDataIdMesg.developerDataIndex === message.developerDataIndex;
446
+ }) ?? {};
447
+
448
+ this.#fieldDescriptionListener(message.key, {...developerDataIdMesg}, {...message});
449
+ }
417
450
  }
418
451
  }
419
452
 
package/src/encoder.js ADDED
@@ -0,0 +1,323 @@
1
+ /////////////////////////////////////////////////////////////////////////////////////////////
2
+ // Copyright 2025 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.161.0Release
9
+ // Tag = production/release/21.161.0-0-g58854c0
10
+ /////////////////////////////////////////////////////////////////////////////////////////////
11
+
12
+
13
+ import CrcCalculator from "./crc-calculator.js";
14
+ import FIT from "./fit.js";
15
+ import MesgDefinition from "./mesg-definition.js";
16
+ import OutputStream from "./output-stream.js";
17
+ import Profile from "./profile.js";
18
+ import Utils from "./utils.js";
19
+
20
+ const HEADER_WITH_CRC_SIZE = 14;
21
+ const HEADER_WITHOUT_CRC_SIZE = 12;
22
+
23
+ /**
24
+ * A class for encoding FIT files.
25
+ * @class
26
+ */
27
+ 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
+ constructor({ fieldDescriptions = null, } = {}) {
35
+ this.#fieldDescriptions = {};
36
+
37
+ for (const [key, {developerDataIdMesg, fieldDescriptionMesg}] of Object.entries(fieldDescriptions ?? {})) {
38
+ this.addDeveloperField(key, developerDataIdMesg, fieldDescriptionMesg);
39
+ }
40
+
41
+ this.#writeEmptyFileHeader();
42
+ }
43
+
44
+ /**
45
+ * Closes the encoder and returns the file data
46
+ * @returns {Uint8Array} A Uint8Array containing the file data
47
+ */
48
+ close() {
49
+ this.#updateFileHeader();
50
+ this.#writeFileCrc();
51
+
52
+ return this.#outputStream.uint8Array;
53
+ }
54
+
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
+ writeMesg(mesg) {
62
+ return this.onMesg(mesg.mesgNum, mesg);
63
+ }
64
+
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
+ onMesg(mesgNum, mesg) {
73
+ try {
74
+ const mesgDefinition = this.#createMesgDefinition(mesgNum, mesg);
75
+ this.#writeMesgDefinitionIfNotActive(mesgDefinition);
76
+
77
+ // Write Message Header
78
+ this.#outputStream.writeUInt8(mesgDefinition.localMesgNum);
79
+
80
+ // Write Field Values
81
+ mesgDefinition.fieldDefinitions.forEach((fieldDefinition) => {
82
+ const values = this.#transformValues(mesg[fieldDefinition.name], fieldDefinition);
83
+ const baseTypeDef = FIT.BaseTypeDefinitions[fieldDefinition.baseType];
84
+
85
+ this.#outputStream.write(values, baseTypeDef.type);
86
+ });
87
+
88
+ // Write Developer Field Values
89
+ mesgDefinition.developerFieldDefinitions.forEach((developerFieldDefinition) => {
90
+ const values = this.#transformValues(
91
+ mesg.developerFields[developerFieldDefinition.key],
92
+ developerFieldDefinition);
93
+
94
+ const baseTypeDef = FIT.BaseTypeDefinitions[developerFieldDefinition.baseType];
95
+
96
+ this.#outputStream.write(values, baseTypeDef.type);
97
+ });
98
+ }
99
+ catch (error) {
100
+ throw new Error(
101
+ "Could not write Message", {
102
+ cause: {
103
+ mesg,
104
+ cause: {
105
+ message: error.message,
106
+ cause: error.cause,
107
+ },
108
+ },
109
+ }
110
+ );
111
+ }
112
+
113
+ return this;
114
+ };
115
+
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
+ addDeveloperField(key, developerDataIdMesg, fieldDescriptionMesg) {
127
+ if(developerDataIdMesg.developerDataIndex == null || fieldDescriptionMesg.developerDataIndex == null) {
128
+ throw new Error("addDeveloperField() - one or more developerDataIndex values are null.", {
129
+ cause: {
130
+ key,
131
+ developerDataIdMesg,
132
+ fieldDescriptionMesg
133
+ }
134
+ });
135
+ }
136
+
137
+ if(developerDataIdMesg.developerDataIndex !== fieldDescriptionMesg.developerDataIndex) {
138
+ throw new Error("addDeveloperField() - developerDataIndex values do not match.", {
139
+ cause: {
140
+ key,
141
+ developerDataIdMesg,
142
+ fieldDescriptionMesg
143
+ }
144
+ });
145
+ }
146
+
147
+ this.#fieldDescriptions[key] = {
148
+ developerDataIdMesg,
149
+ fieldDescriptionMesg
150
+ }
151
+
152
+ return this;
153
+ }
154
+
155
+ #writeEmptyFileHeader() {
156
+ Array(HEADER_WITH_CRC_SIZE).fill(0).forEach((zero) => {
157
+ this.#outputStream.writeUInt8(zero);
158
+ });
159
+ }
160
+
161
+ #updateFileHeader() {
162
+ const arrayBuffer = new ArrayBuffer(HEADER_WITH_CRC_SIZE);
163
+ const dataView = new DataView(arrayBuffer);
164
+
165
+ // Header Size
166
+ dataView.setUint8(0, HEADER_WITH_CRC_SIZE);
167
+
168
+ // Protocol Version
169
+ dataView.setUint8(1, 2);
170
+
171
+ // Profile Version
172
+ dataView.setUint16(2, Profile.version.major * 1000 + Profile.version.minor, true);
173
+
174
+ // Data Size
175
+ dataView.setUint32(4, this.#outputStream.length - HEADER_WITH_CRC_SIZE, true);
176
+
177
+ // Data Type ".FIT"
178
+ dataView.setUint8(8, 0x2E);
179
+ dataView.setUint8(9, 0x46);
180
+ dataView.setUint8(10, 0x49);
181
+ dataView.setUint8(11, 0x54);
182
+
183
+ // Header CRC
184
+ const crc = CrcCalculator.calculateCRC(new Uint8Array(arrayBuffer), 0, HEADER_WITHOUT_CRC_SIZE);
185
+ dataView.setUint16(12, crc, true);
186
+
187
+ this.#outputStream.set(new Uint8Array(arrayBuffer));
188
+ }
189
+
190
+ #writeFileCrc() {
191
+ const crc = CrcCalculator.calculateCRC(this.#outputStream.uint8Array, 0, this.#outputStream.length);
192
+ this.#outputStream.writeUInt16(crc);
193
+ }
194
+
195
+ #transformValues(value, fieldDefinition) {
196
+ const values = Array.isArray(value) ? value : [value,];
197
+
198
+ return values.map((value) => {
199
+ return this.#transformValue(value, fieldDefinition);
200
+ });
201
+ }
202
+
203
+ #transformValue(value, fieldDefinition) {
204
+ try {
205
+ if (FIT.isNotNumberStringDateOrBoolean(value)) {
206
+ return FIT.BaseTypeDefinitions[fieldDefinition.baseType].invalid;
207
+ }
208
+
209
+ // Is this a numeric field?
210
+ if (FIT.NumericFieldTypes.includes(fieldDefinition.type)) {
211
+ if (!FIT.isNumeric(value)) {
212
+ throw new Error();
213
+ }
214
+
215
+ const scale = Array.isArray(fieldDefinition.scale) ? fieldDefinition.scale[0] : fieldDefinition.scale;
216
+ const offset = Array.isArray(fieldDefinition.offset) ? fieldDefinition.offset[0] : fieldDefinition.offset;
217
+
218
+ return (value + offset) * scale;
219
+ }
220
+
221
+ // Is this a date_time field?
222
+ if (fieldDefinition.type === "dateTime") {
223
+ if (FIT.isDate(value)) {
224
+ return Utils.convertDateToDateTime(value);
225
+ }
226
+
227
+ if (!FIT.isNumeric(value)) {
228
+ throw new Error();
229
+ }
230
+
231
+ return value;
232
+ }
233
+
234
+ // Is this a string field
235
+ if (fieldDefinition.type === "string") {
236
+ if (!FIT.isString(value)) {
237
+ throw new Error();
238
+ }
239
+
240
+ return value;
241
+ }
242
+
243
+ // Must be a FIT type field
244
+ if (FIT.isNumeric(value)) {
245
+ return value;
246
+ }
247
+
248
+ const profileType = Profile.types[fieldDefinition.type];
249
+
250
+ const [typeValue,] = Object.entries(profileType).find(([, typeValue,]) => {
251
+ return typeValue === value;
252
+ });
253
+
254
+ return typeValue;
255
+ }
256
+ catch {
257
+ throw new Error(
258
+ `Could not convert "${value}" to "${fieldDefinition.type}"`,
259
+ { cause: { value, fieldDefinition, }, });
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Creates a MesgDefinition from the mesgNum and mesg.
265
+ * @param {Number} mesgNum - The mesg number for this message
266
+ * @param {Object} [mesg] - The message data
267
+ * @return {MesgDefinition}
268
+ */
269
+ #createMesgDefinition = (mesgNum, mesg) => {
270
+ const mesgDefinition = new MesgDefinition(mesgNum, mesg, { fieldDescriptions: this.#fieldDescriptions, });
271
+ mesgDefinition.localMesgNum = this.#lookupLocalMesgNum(mesgDefinition);
272
+
273
+ return mesgDefinition;
274
+ };
275
+
276
+ /**
277
+ * Searches the #localMesgDefinitions for a matching mesgDefinition
278
+ * @param {Object} mesgDefinition - the mesg definition to match
279
+ * @return The localMesgNum to be used with mesgDefinition
280
+ */
281
+ #lookupLocalMesgNum = (mesgDefinition) => {
282
+ const localMesgNum = this.#localMesgDefinitions.findIndex((localMesgDefinition) => {
283
+ return localMesgDefinition?.equals(mesgDefinition) ?? false;
284
+ });
285
+
286
+ return (localMesgNum !== -1 ? localMesgNum : this.#nextLocalMesgNum++) & FIT.LOCAL_MESG_NUM_MASK;
287
+ };
288
+
289
+ /**
290
+ * Writes the mesgDefinition to the output stream, if it is not one of the currently active 16
291
+ * @param {Object} mesgDefinition - the mesg definition to match
292
+ * @return The localMesgNum to be used with mesgDefinition
293
+ */
294
+ #writeMesgDefinitionIfNotActive = (mesgDefinition) => {
295
+ const localMesgNum = mesgDefinition.localMesgNum;
296
+
297
+ if (this.#localMesgDefinitions[localMesgNum] == null
298
+ || !this.#localMesgDefinitions[localMesgNum].equals(mesgDefinition)) {
299
+ this.#writeMesgDefinition(mesgDefinition);
300
+ }
301
+
302
+ return localMesgNum;
303
+ };
304
+
305
+ /**
306
+ * Writes the mesgDefinition to the output stream
307
+ * @param {Object} mesgDefinition - the mesg definition to write
308
+ * @return {this}
309
+ */
310
+ #writeMesgDefinition(mesgDefinition) {
311
+ mesgDefinition.write(this.#outputStream);
312
+ this.#localMesgDefinitions[mesgDefinition.localMesgNum] = mesgDefinition;
313
+
314
+ return this;
315
+ }
316
+
317
+ #localMesgDefinitions = Array(16).fill(null);
318
+ #nextLocalMesgNum = 0;
319
+ #outputStream = new OutputStream();
320
+ #fieldDescriptions = null;
321
+ }
322
+
323
+ export default Encoder;
package/src/fit.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /////////////////////////////////////////////////////////////////////////////////////////////
2
- // Copyright 2024 Garmin International, Inc.
2
+ // Copyright 2025 Garmin International, Inc.
3
3
  // Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
4
4
  // may not use this file except in compliance with the Flexible and Interoperable Data
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.158.0Release
9
- // Tag = production/release/21.158.0-0-gc9428aa
8
+ // Profile Version = 21.161.0Release
9
+ // Tag = production/release/21.161.0-0-g58854c0
10
10
  /////////////////////////////////////////////////////////////////////////////////////////////
11
11
 
12
12
 
@@ -69,6 +69,7 @@ const NumericFieldTypes = [
69
69
  ];
70
70
 
71
71
  const FieldTypeToBaseType = {
72
+ "enum": BaseType.UINT8,
72
73
  "sint8": BaseType.SINT8,
73
74
  "uint8": BaseType.UINT8,
74
75
  "sint16": BaseType.SINT16,
@@ -87,9 +88,83 @@ const FieldTypeToBaseType = {
87
88
  "uint64z": BaseType.UINT64Z
88
89
  };
89
90
 
91
+ const BaseTypeToFieldType = {
92
+ [BaseType.ENUM]: "enum",
93
+ [BaseType.SINT8]: "sint8",
94
+ [BaseType.UINT8]: "uint8",
95
+ [BaseType.SINT16]: "sint16",
96
+ [BaseType.UINT16]: "uint16",
97
+ [BaseType.SINT32]: "sint32",
98
+ [BaseType.UINT32]: "uint32",
99
+ [BaseType.STRING]: "string",
100
+ [BaseType.FLOAT32]: "float32",
101
+ [BaseType.FLOAT64]: "float64",
102
+ [BaseType.UINT8Z]: "uint8z",
103
+ [BaseType.UINT16Z]: "uint16z",
104
+ [BaseType.UINT32Z]: "uint32z",
105
+ [BaseType.BYTE]: "byte",
106
+ [BaseType.SINT64]: "sint64",
107
+ [BaseType.UINT64]: "uint64",
108
+ [BaseType.UINT64Z]: "uint64z",
109
+ };
110
+
111
+ const isNullOrUndefined = (obj) => {
112
+ return obj == null;
113
+ };
114
+
115
+ const isObject = (obj) => {
116
+ return typeof obj === "object";
117
+ };
118
+
119
+ const isBoolean = (obj) => {
120
+ return "boolean" === typeof obj;
121
+ };
122
+
123
+ const isDate = (obj) => {
124
+ return typeof obj === "object" && obj instanceof Date;
125
+ };
126
+
127
+ const isString = (obj) => {
128
+ return typeof obj === "string";
129
+ };
130
+
131
+ const isNumeric = (obj) => {
132
+ return !isNaN(parseFloat(obj)) && isFinite(obj);
133
+ };
134
+
135
+ const isNotNumberStringDateOrBoolean = (obj) => {
136
+ return !isNumberStringDateOrBoolean(obj);
137
+ };
138
+
139
+ const isNumberStringDateOrBoolean = (obj) => {
140
+ if (isNullOrUndefined(obj)) {
141
+ return false;
142
+ }
143
+
144
+ if (!isDate(obj) && !isString(obj) && !isNumeric(obj) && !isBoolean(obj)) {
145
+ return false;
146
+ }
147
+
148
+ return true;
149
+ };
150
+
90
151
  export default {
91
152
  BaseType,
92
153
  BaseTypeDefinitions,
93
154
  NumericFieldTypes,
94
- FieldTypeToBaseType
155
+ FieldTypeToBaseType,
156
+ BaseTypeToFieldType,
157
+ isNullOrUndefined,
158
+ isObject,
159
+ isBoolean,
160
+ isDate,
161
+ isString,
162
+ isNumeric,
163
+ isNumberStringDateOrBoolean,
164
+ isNotNumberStringDateOrBoolean,
165
+ MAX_FIELD_SIZE: 255,
166
+ MESG_DEFINITION_MASK: 0x40,
167
+ LOCAL_MESG_NUM_MASK: 0x0F,
168
+ ARCH_LITTLE_ENDIAN: 0x00,
169
+ DEV_DATA_MASK: 0x20,
95
170
  };
package/src/index.js CHANGED
@@ -1,18 +1,20 @@
1
1
  /////////////////////////////////////////////////////////////////////////////////////////////
2
- // Copyright 2024 Garmin International, Inc.
2
+ // Copyright 2025 Garmin International, Inc.
3
3
  // Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
4
4
  // may not use this file except in compliance with the Flexible and Interoperable Data
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.158.0Release
9
- // Tag = production/release/21.158.0-0-gc9428aa
8
+ // Profile Version = 21.161.0Release
9
+ // Tag = production/release/21.161.0-0-g58854c0
10
10
  /////////////////////////////////////////////////////////////////////////////////////////////
11
11
 
12
12
 
13
+ import CrcCalculator from "./crc-calculator.js";
13
14
  import Decoder from "./decoder.js";
15
+ import Encoder from "./encoder.js";
14
16
  import Profile from "./profile.js";
15
17
  import Stream from "./stream.js";
16
18
  import Utils from "./utils.js";
17
19
 
18
- export { Decoder, Stream, Profile, Utils };
20
+ export { CrcCalculator, Decoder, Encoder, Stream, Profile, Utils };