@garmin/fitsdk 21.158.0 → 21.168.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.
@@ -0,0 +1,270 @@
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.168.0Release
9
+ // Tag = production/release/21.168.0-0-gb831b31
10
+ /////////////////////////////////////////////////////////////////////////////////////////////
11
+
12
+
13
+ import FIT from "./fit.js";
14
+ import Profile from "./profile.js";
15
+
16
+ const textEncoder = new TextEncoder();
17
+
18
+ class MesgDefinition {
19
+ globalMesgNumber;
20
+ localMesgNum;
21
+ fieldDefinitions = [];
22
+ developerFieldDefinitions = [];
23
+
24
+ constructor(mesgNum, mesg, { fieldDescriptions = null, } = {}) {
25
+ try {
26
+ if (mesg == null) {
27
+ throw new Error("mesg is missing or null");
28
+ }
29
+
30
+ if (mesgNum == null) {
31
+ throw new Error("mesgNum is missing or null");
32
+ }
33
+
34
+ const mesgProfile = Profile.messages[mesgNum];
35
+
36
+ if (mesgProfile == null) {
37
+ throw new Error(`mesgNum: ${mesgNum} could not be found in the Profile`);
38
+ }
39
+
40
+ this.globalMesgNumber = mesgNum;
41
+ this.localMesgNum = 0;
42
+
43
+ Object.keys(mesg).forEach((fieldName) => {
44
+ if (mesg[fieldName] == null) {
45
+ return;
46
+ }
47
+
48
+ const fieldProfile = Object.entries(mesgProfile.fields).find(([, fieldProfile,]) => {
49
+ return fieldProfile.name === fieldName;
50
+ });
51
+
52
+ if (fieldProfile == null) {
53
+ return;
54
+ }
55
+
56
+ const baseType = FIT.FieldTypeToBaseType[fieldProfile[1].baseType];
57
+ const baseTypeDef = FIT.BaseTypeDefinitions[baseType];
58
+
59
+ this.fieldDefinitions.push({
60
+ name: fieldName,
61
+ num: fieldProfile[1].num,
62
+ size: this.#fieldSize(mesg[fieldName], baseTypeDef),
63
+ baseType: baseType,
64
+ type: fieldProfile[1].type,
65
+ scale: fieldProfile[1].scale,
66
+ offset: fieldProfile[1].offset,
67
+ components: fieldProfile[1].components,
68
+ });
69
+ });
70
+
71
+ Object.keys(mesg.developerFields ?? {})?.sort()?.forEach((key) => {
72
+ const { developerDataIdMesg, fieldDescriptionMesg, } = this.#fieldDescriptionForKey(fieldDescriptions, key);
73
+
74
+ const baseTypeDef = FIT.BaseTypeDefinitions[fieldDescriptionMesg.fitBaseTypeId];
75
+
76
+ this.developerFieldDefinitions.push({
77
+ key,
78
+ baseType: fieldDescriptionMesg.fitBaseTypeId,
79
+ fieldDefinitionNumber: fieldDescriptionMesg.fieldDefinitionNumber,
80
+ size: this.#fieldSize(mesg.developerFields[key], baseTypeDef),
81
+ developerDataIndex: developerDataIdMesg.developerDataIndex,
82
+ });
83
+ });
84
+
85
+ if (this.fieldDefinitions.length === 0) {
86
+ throw new Error("No valid fields were found in the message");
87
+ }
88
+
89
+ if (this.fieldDefinitions.some((fieldDefinition) => {
90
+ return fieldDefinition.size > FIT.MAX_FIELD_SIZE;
91
+ })) {
92
+ throw new Error(`Some field sizes are greater than ${FIT.MAX_FIELD_SIZE}`, { cause: this.fieldDefinitions, });
93
+ }
94
+ }
95
+ catch (error) {
96
+ throw new Error(
97
+ "Could not construct MesgDefinition from Message", {
98
+ cause: {
99
+ cause: {
100
+ message: error.message,
101
+ cause: error.cause,
102
+ },
103
+ },
104
+ }
105
+ );
106
+ }
107
+ }
108
+
109
+ write(outputStream) {
110
+ // Header
111
+ let headerByte = FIT.MESG_DEFINITION_MASK | (this.localMesgNum & FIT.LOCAL_MESG_NUM_MASK);
112
+ if (this.developerFieldDefinitions.length > 0) {
113
+ headerByte |= FIT.DEV_DATA_MASK;
114
+ }
115
+
116
+ outputStream.writeUInt8(headerByte);
117
+
118
+ // Reserved Byte
119
+ outputStream.writeUInt8(0x00);
120
+
121
+ // Architecture
122
+ outputStream.writeUInt8(FIT.ARCH_LITTLE_ENDIAN);
123
+
124
+ // Global Message Number
125
+ outputStream.writeUInt16(this.globalMesgNumber);
126
+
127
+ // Field Count
128
+ outputStream.writeUInt8(this.fieldDefinitions.length);
129
+
130
+ // Field Definitions
131
+ this.fieldDefinitions.forEach((fieldDefinition) => {
132
+ outputStream.writeUInt8(fieldDefinition.num);
133
+ outputStream.writeUInt8(fieldDefinition.size);
134
+ outputStream.writeUInt8(fieldDefinition.baseType);
135
+ });
136
+
137
+ // Developer Field Definitions
138
+ if (this.developerFieldDefinitions.length > 0) {
139
+ outputStream.writeUInt8(this.developerFieldDefinitions.length);
140
+
141
+ this.developerFieldDefinitions.forEach((developerFieldDefinition) => {
142
+ outputStream.writeUInt8(developerFieldDefinition.fieldDefinitionNumber);
143
+ outputStream.writeUInt8(developerFieldDefinition.size);
144
+ outputStream.writeUInt8(developerFieldDefinition.developerDataIndex);
145
+ });
146
+ }
147
+ }
148
+
149
+ equals(other) {
150
+ if (this.globalMessageNumber !== other.globalMessageNumber
151
+ || this.fieldDefinitions.length !== other.fieldDefinitions.length
152
+ || this.developerFieldDefinitions.length !== other.developerFieldDefinitions.length) {
153
+ return false;
154
+ }
155
+
156
+ // Field Definitions
157
+ for (let i = 0; i < this.fieldDefinitions.length; i++) {
158
+ const lhs = this.fieldDefinitions[i];
159
+
160
+ if (null == other.fieldDefinitions.find((rhs) => {
161
+ return lhs.num === rhs.num
162
+ && lhs.size === rhs.size
163
+ && lhs.baseType === rhs.baseType;
164
+ })) {
165
+ return false;
166
+ }
167
+ }
168
+
169
+ // Developer Field Definitions
170
+ for (let i = 0; i < this.developerFieldDefinitions.length; i++) {
171
+ const lhs = this.developerFieldDefinitions[i];
172
+
173
+ if (null == other.developerFieldDefinitions.find((rhs) => {
174
+ return lhs.fieldDefinitionNumber === rhs.fieldDefinitionNumber
175
+ && lhs.size === rhs.size
176
+ && lhs.developerDataIndex === rhs.developerDataIndex;
177
+ })) {
178
+ return false;
179
+ }
180
+ }
181
+
182
+ return true;
183
+ }
184
+
185
+ #fieldSize(value, baseTypeDef) {
186
+ const values = Array.isArray(value) ? value : [value,];
187
+
188
+ if (baseTypeDef.type === FIT.BaseType.STRING) {
189
+ const size = values.reduce(
190
+ (accumulator, currentValue) => {
191
+ return accumulator + textEncoder.encode(currentValue).length + 1;
192
+ },
193
+ 0
194
+ );
195
+
196
+ return size;
197
+ }
198
+
199
+ return baseTypeDef.size * values.length;
200
+ };
201
+
202
+ /**
203
+ * Look up the field description for the key, and validate the required fields.
204
+ */
205
+ #fieldDescriptionForKey(fieldDescriptions, key) {
206
+ try {
207
+ if (fieldDescriptions == null) {
208
+ throw new Error("no developer data field descriptions provided", {
209
+ cause: {
210
+ fieldDescriptions,
211
+ },
212
+ });
213
+ }
214
+
215
+ const { developerDataIdMesg, fieldDescriptionMesg, } = fieldDescriptions?.[key] ?? {};
216
+
217
+ if (developerDataIdMesg == null || fieldDescriptionMesg == null) {
218
+ throw new Error(`could not find a developer field description for key ${key}`);
219
+ }
220
+
221
+ const errors = [];
222
+
223
+ if (fieldDescriptionMesg.fitBaseTypeId == null) {
224
+ errors.push(`fieldDescriptionMesg fitBaseTypeId is ${fieldDescriptionMesg.fitBaseTypeId}`);
225
+ }
226
+ if (fieldDescriptionMesg.fieldDefinitionNumber == null) {
227
+ errors.push(`fieldDescriptionMesg fieldDefinitionNumber is ${fieldDescriptionMesg.fieldDefinitionNumber}`);
228
+ }
229
+ if (fieldDescriptionMesg.developerDataIndex == null) {
230
+ errors.push(`fieldDescriptionMesg developerDataIndex is ${fieldDescriptionMesg.developerDataIndex}`);
231
+ }
232
+ if (developerDataIdMesg.developerDataIndex == null) {
233
+ errors.push(`developerDataIdMesg developerDataIndex is ${developerDataIdMesg.developerDataIndex}`);
234
+ }
235
+
236
+ if (developerDataIdMesg.developerDataIndex !== fieldDescriptionMesg.developerDataIndex) {
237
+ errors.push("developerDataIndex values do not match in fieldDescription"
238
+ + ` ${developerDataIdMesg.developerDataIndex} != ${fieldDescriptionMesg.developerDataIndex}`
239
+ );
240
+ }
241
+
242
+ if (errors.length > 0) {
243
+ throw new Error("missing or invalid values in the fieldDescription,", {
244
+ cause: {
245
+ developerDataIdMesg,
246
+ fieldDescriptionMesg,
247
+ cause: errors,
248
+ },
249
+ });
250
+ }
251
+
252
+ return { key, developerDataIdMesg, fieldDescriptionMesg, };
253
+ }
254
+ catch (error) {
255
+ throw new Error(
256
+ `invalid field description for key ${key}`, {
257
+ cause: {
258
+ key,
259
+ cause: {
260
+ message: error.message,
261
+ cause: error.cause,
262
+ },
263
+ },
264
+ }
265
+ );
266
+ }
267
+ }
268
+ }
269
+
270
+ export default MesgDefinition;
@@ -0,0 +1,220 @@
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.168.0Release
9
+ // Tag = production/release/21.168.0-0-gb831b31
10
+ /////////////////////////////////////////////////////////////////////////////////////////////
11
+
12
+
13
+ import FIT from "./fit.js";
14
+
15
+ const ONE_MEGABYTE = 1048576;
16
+ const TEN_MEGABYTES = ONE_MEGABYTE * 10;
17
+ const HALF_MEGABYTE = ONE_MEGABYTE / 2;
18
+
19
+ class OutputStream {
20
+ #arrayBuffer = null;
21
+ #dataView = null;
22
+ #byteOffset = 0;
23
+ #resizeByBytes = 0;
24
+ #baseTypeDefinitions = null;
25
+ #textEncoder = new TextEncoder();
26
+
27
+ /**
28
+ * Creates an OutputStream
29
+ * @constructor
30
+ * @param {Object=} [options] - Read options (optional)
31
+ * @param {Number} [options.initialByteLength=0.5MB] - (optional, default 0.5 MB)
32
+ * @param {Number} [options.maxByteLength=2MB] - (optional, default 2 MB)
33
+ * @param {Number} [options.resizeByBytes=0.5MB] - (optional, default 0.5 MB)
34
+ */
35
+ constructor({
36
+ initialByteLength = HALF_MEGABYTE,
37
+ maxByteLength = TEN_MEGABYTES,
38
+ resizeByBytes = HALF_MEGABYTE,
39
+ } = {}) {
40
+ this.#arrayBuffer = new ArrayBuffer(initialByteLength, { maxByteLength, });
41
+ this.#dataView = new DataView(this.#arrayBuffer);
42
+
43
+ this.#resizeByBytes = resizeByBytes;
44
+
45
+ this.#baseTypeDefinitions = {
46
+ [FIT.BaseType.ENUM]: { setValue: this.#dataView.setUint8.bind(this.#dataView), size: 1, mask: 0xFF, },
47
+ [FIT.BaseType.UINT8]: { setValue: this.#dataView.setUint8.bind(this.#dataView), size: 1, mask: 0xFF, },
48
+ [FIT.BaseType.UINT16]: { setValue: this.#dataView.setUint16.bind(this.#dataView), size: 2, mask: 0xFFFF, },
49
+ [FIT.BaseType.UINT32]: { setValue: this.#dataView.setUint32.bind(this.#dataView), size: 4, mask: 0xFFFFFFFF, },
50
+ [FIT.BaseType.UINT64]: { setValue: this.#dataView.setBigUint64.bind(this.#dataView), size: 8, mask: 0xFFFFFFFFFFFFFFFFn, },
51
+ [FIT.BaseType.SINT8]: { setValue: this.#dataView.setInt8.bind(this.#dataView), size: 1, mask: 0xFF, },
52
+ [FIT.BaseType.SINT16]: { setValue: this.#dataView.setInt16.bind(this.#dataView), size: 2, mask: 0xFFFF, },
53
+ [FIT.BaseType.SINT32]: { setValue: this.#dataView.setInt32.bind(this.#dataView), size: 4, mask: 0xFFFFFFFF, },
54
+ [FIT.BaseType.SINT64]: { setValue: this.#dataView.setBigInt64.bind(this.#dataView), size: 8, mask: 0xFFFFFFFFFFFFFFFFn, },
55
+ [FIT.BaseType.FLOAT32]: { setValue: this.#dataView.setFloat32.bind(this.#dataView), size: 4, },
56
+ [FIT.BaseType.FLOAT64]: { setValue: this.#dataView.setFloat64.bind(this.#dataView), size: 8, },
57
+ [FIT.BaseType.UINT8Z]: { setValue: this.#dataView.setUint8.bind(this.#dataView), size: 1, mask: 0xFF, },
58
+ [FIT.BaseType.UINT16Z]: { setValue: this.#dataView.setUint16.bind(this.#dataView), size: 2, mask: 0xFFFF, },
59
+ [FIT.BaseType.UINT32Z]: { setValue: this.#dataView.setUint32.bind(this.#dataView), size: 4, mask: 0xFFFFFFFF, },
60
+ [FIT.BaseType.UINT64Z]: { setValue: this.#dataView.setBigUint64.bind(this.#dataView), size: 8, mask: 0xFFFFFFFFFFFFFFFFn, },
61
+ [FIT.BaseType.BYTE]: { setValue: this.#dataView.setUint8.bind(this.#dataView), size: 1, mask: 0xFF, },
62
+ };
63
+ }
64
+
65
+ get length() {
66
+ return this.#byteOffset;
67
+ }
68
+ get uint8Array() {
69
+ return new Uint8Array(this.#arrayBuffer.slice(0, this.#byteOffset));
70
+ }
71
+
72
+ writeUInt8(value) {
73
+ return this.write(value, FIT.BaseType.UINT8);
74
+ }
75
+
76
+ writeUInt16(value) {
77
+ return this.write(value, FIT.BaseType.UINT16);
78
+ }
79
+
80
+ writeUInt32(value) {
81
+ return this.write(value, FIT.BaseType.UINT32);
82
+ }
83
+
84
+ writeUInt64(value) {
85
+ return this.write(value, FIT.BaseType.UINT64);
86
+ }
87
+
88
+ writeSInt8(value) {
89
+ return this.write(value, FIT.BaseType.SINT8);
90
+ }
91
+
92
+ writeSInt16(value) {
93
+ return this.write(value, FIT.BaseType.SINT16);
94
+ }
95
+
96
+ writeSInt32(value) {
97
+ return this.write(value, FIT.BaseType.SINT32);
98
+ }
99
+
100
+ writeSInt64(value) {
101
+ return this.write(value, FIT.BaseType.SINT64);
102
+ }
103
+
104
+ writeFloat32(value) {
105
+ return this.write(value, FIT.BaseType.FLOAT32);
106
+ }
107
+
108
+ writeFloat64(value) {
109
+ return this.write(value, FIT.BaseType.FLOAT64);
110
+ }
111
+
112
+ writeUInt8z(value) {
113
+ return this.write(value, FIT.BaseType.UINT8Z);
114
+ }
115
+
116
+ writeUInt16z(value) {
117
+ return this.write(value, FIT.BaseType.UINT16Z);
118
+ }
119
+
120
+ writeUInt32z(value) {
121
+ return this.write(value, FIT.BaseType.UINT32Z);
122
+ }
123
+
124
+ writeUInt64z(value) {
125
+ return this.write(value, FIT.BaseType.UINT64Z);
126
+ }
127
+
128
+ writeByte(value) {
129
+ return this.write(value, FIT.BaseType.BYTE);
130
+ }
131
+
132
+ writeString(text) {
133
+ const bytes = this.#textEncoder.encode(text);
134
+
135
+ this.#resizeIfNeeded(bytes.byteLength);
136
+
137
+ const uint8Array = new Uint8Array(this.#arrayBuffer, this.#byteOffset, bytes.byteLength);
138
+ uint8Array.set(bytes);
139
+
140
+ this.#byteOffset += bytes.byteLength;
141
+
142
+ // Add a null terminator
143
+ this.writeUInt8(0);
144
+
145
+ return this;
146
+ }
147
+
148
+ write(value, baseType) {
149
+ if (baseType === FIT.BaseType.STRING) {
150
+ return this.writeString(value);
151
+ }
152
+
153
+ this.#setValues(baseType, value);
154
+
155
+ return;
156
+ }
157
+
158
+ set(typedarray, targetOffset = 0) {
159
+ this.#resizeIfNeeded(typedarray.byteLength + targetOffset);
160
+
161
+ const uint8Array = new Uint8Array(this.#arrayBuffer);
162
+ uint8Array.set(typedarray, targetOffset);
163
+
164
+ this.#byteOffset = Math.max(this.#byteOffset, typedarray.byteLength + targetOffset);
165
+
166
+ return this;
167
+ }
168
+
169
+ [Symbol.iterator]() {
170
+ let start = 0;
171
+ const end = this.#byteOffset;
172
+ const dataView = this.#dataView;
173
+
174
+ return {
175
+ next() {
176
+ if (start < end) {
177
+ return { value: dataView.getUint8(start++), done: false, };
178
+ }
179
+ else {
180
+ return { done: true, };
181
+ }
182
+ },
183
+ };
184
+ }
185
+
186
+ #setValues(baseType, value) {
187
+ const values = Array.isArray(value) ? value : [value,];
188
+
189
+ values.forEach((value) => {
190
+ return this.#setValue(baseType, value);
191
+ });
192
+ }
193
+
194
+ #setValue(baseType, value) {
195
+ const def = this.#baseTypeDefinitions[baseType];
196
+
197
+ this.#resizeIfNeeded(def.size);
198
+
199
+ const val = def.mask == null ? value : value & def.mask;
200
+
201
+ def.setValue(this.#byteOffset, val, true);
202
+ this.#byteOffset += def.size;
203
+
204
+ return this;
205
+ }
206
+
207
+ #resizeIfNeeded(byteCount = 1) {
208
+ if (this.#arrayBuffer.byteLength - this.#byteOffset >= byteCount) {
209
+ return;
210
+ }
211
+
212
+ if (!this.#arrayBuffer.resizable) {
213
+ throw new Error("Can not resize OutputStream. Set a larger initial size.");
214
+ }
215
+
216
+ this.#arrayBuffer.resize(this.#arrayBuffer.byteLength + Math.max(this.#resizeByBytes, byteCount));
217
+ }
218
+ }
219
+
220
+ export default OutputStream;