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.
@@ -2,21 +2,30 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.mapDataIntoLap = mapDataIntoLap;
4
4
  exports.mapDataIntoSession = mapDataIntoSession;
5
+ function timestampValue(value) {
6
+ if (value === undefined || value === null) {
7
+ return undefined;
8
+ }
9
+ const timestamp = value instanceof Date
10
+ ? value.getTime()
11
+ : new Date(value).getTime();
12
+ return Number.isFinite(timestamp) ? timestamp : undefined;
13
+ }
5
14
  function mapDataIntoLap(inputLaps, lapKey, data) {
15
+ var _a;
6
16
  const laps = [...inputLaps];
7
17
  let index = 0;
8
18
  for (let i = 0; i < laps.length; i++) {
9
19
  const nextLap = laps[i + 1];
10
20
  const tempData = [];
11
- const nextLapStartTime = nextLap
12
- ? new Date(nextLap.start_time).getTime()
13
- : null;
21
+ const nextLapStartTime = timestampValue(nextLap === null || nextLap === void 0 ? void 0 : nextLap.start_time);
14
22
  for (let j = index; j < data.length; j++) {
15
23
  const row = data[j];
16
- if (nextLap && nextLapStartTime) {
17
- const timestamp = new Date(row.timestamp || row.start_time).getTime();
18
- if (nextLapStartTime > timestamp) {
24
+ if (nextLap && nextLapStartTime !== undefined) {
25
+ const timestamp = timestampValue((_a = row.timestamp) !== null && _a !== void 0 ? _a : row.start_time);
26
+ if (timestamp === undefined || nextLapStartTime > timestamp) {
19
27
  tempData.push(row);
28
+ index = j + 1;
20
29
  }
21
30
  else if (nextLapStartTime <= timestamp) {
22
31
  index = j;
@@ -25,6 +34,7 @@ function mapDataIntoLap(inputLaps, lapKey, data) {
25
34
  }
26
35
  else {
27
36
  tempData.push(row);
37
+ index = j + 1;
28
38
  }
29
39
  }
30
40
  if (!laps[i][lapKey]) {
@@ -39,15 +49,14 @@ function mapDataIntoSession(inputSessions, laps) {
39
49
  for (let i = 0; i < sessions.length; i++) {
40
50
  const nextSession = sessions[i + 1];
41
51
  const tempLaps = [];
42
- const nextSessionStartTime = nextSession
43
- ? new Date(nextSession.start_time).getTime()
44
- : null;
52
+ const nextSessionStartTime = timestampValue(nextSession === null || nextSession === void 0 ? void 0 : nextSession.start_time);
45
53
  for (let j = lapIndex; j < laps.length; j++) {
46
54
  const lap = laps[j];
47
- if (nextSession && nextSessionStartTime) {
48
- const lapStartTime = new Date(lap.start_time).getTime();
49
- if (nextSessionStartTime > lapStartTime) {
55
+ if (nextSession && nextSessionStartTime !== undefined) {
56
+ const lapStartTime = timestampValue(lap.start_time);
57
+ if (lapStartTime === undefined || nextSessionStartTime > lapStartTime) {
50
58
  tempLaps.push(lap);
59
+ lapIndex = j + 1;
51
60
  }
52
61
  else if (nextSessionStartTime <= lapStartTime) {
53
62
  lapIndex = j;
@@ -56,6 +65,7 @@ function mapDataIntoSession(inputSessions, laps) {
56
65
  }
57
66
  else {
58
67
  tempLaps.push(lap);
68
+ lapIndex = j + 1;
59
69
  }
60
70
  }
61
71
  if (!sessions[i].laps) {
@@ -1,7 +1,6 @@
1
- import type { MessageObject } from './fit.js';
2
- import type { MesgNum } from './fit_types.js';
1
+ import type { MessageName, MessageObject } from './fit.js';
3
2
  export declare function getFitMessage(messageNum: number): {
4
- name: MesgNum | '';
3
+ name: MessageName | '';
5
4
  getAttributes: (fieldNum: number) => MessageObject;
6
5
  };
7
6
  export declare function getFitMessageBaseType(inp: any): any;
@@ -19,6 +19,9 @@ export declare function generateOptions(options: {
19
19
  }): ts.Node[];
20
20
  export declare function generateUtilities(): Statement[];
21
21
  export declare function generateFitType(): Statement;
22
+ export declare function generateParsedMessages(messages: {
23
+ [messageId: number]: Message;
24
+ }): Statement;
22
25
  export declare function generateMessages(messages: {
23
26
  [messageId: number]: Message;
24
27
  }): Statement[];
@@ -16,6 +16,7 @@ exports.generateTypes = generateTypes;
16
16
  exports.generateOptions = generateOptions;
17
17
  exports.generateUtilities = generateUtilities;
18
18
  exports.generateFitType = generateFitType;
19
+ exports.generateParsedMessages = generateParsedMessages;
19
20
  exports.generateMessages = generateMessages;
20
21
  exports.main = main;
21
22
  const typescript_1 = __importDefault(require("typescript"));
@@ -58,7 +59,16 @@ function generateProperty(name, type, optional = true) {
58
59
  function generateArrayProperty(name, type, optional = true) {
59
60
  return generateProperty(name, typescript_1.default.factory.createArrayTypeNode(type), optional);
60
61
  }
62
+ function generateNullableArrayType(type) {
63
+ return typescript_1.default.factory.createArrayTypeNode(typescript_1.default.factory.createParenthesizedType(typescript_1.default.factory.createUnionTypeNode([
64
+ type,
65
+ typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createNull()),
66
+ ])));
67
+ }
61
68
  function generateTypeFromField(def) {
69
+ if (def.array === true) {
70
+ return generateNullableArrayType(generateTypeFromField(Object.assign(Object.assign({}, def), { array: false })));
71
+ }
62
72
  switch (def.type) {
63
73
  case 'uint32_array':
64
74
  case 'uint16_array':
@@ -67,12 +77,15 @@ function generateTypeFromField(def) {
67
77
  case 'sint16_array':
68
78
  case 'sint8_array':
69
79
  case 'byte_array':
70
- return typescript_1.default.factory.createArrayTypeNode(typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword));
71
- case 'bool':
72
- return typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword);
80
+ return generateNullableArrayType(typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword));
81
+ case 'exercise_category_array':
82
+ return generateNullableArrayType(typescript_1.default.factory.createTypeReferenceNode('ExerciseCategory'));
73
83
  case 'date_time':
84
+ case 'local_date_time':
85
+ return typescript_1.default.factory.createTypeReferenceNode('Date');
74
86
  case 'string':
75
87
  return typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.StringKeyword);
88
+ case 'bool':
76
89
  case 'uint32':
77
90
  case 'uint64':
78
91
  case 'uint16':
@@ -102,18 +115,30 @@ function generateTypes(types) {
102
115
  const nodes = [];
103
116
  const typeNames = Object.keys(types);
104
117
  typeNames.forEach((name) => {
105
- if (name === 'message_index') {
106
- return;
107
- }
108
118
  const type = types[name];
109
- const names = Object.values(type);
119
+ const names = [...new Set(Object.values(type))];
120
+ const isMask = names.includes('mask');
121
+ const typeNode = isMask
122
+ ? typescript_1.default.factory.createTypeLiteralNode([
123
+ generateProperty('value', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword), false),
124
+ ...names
125
+ .filter(value => value !== 'mask')
126
+ .map(value => generateProperty(String(value), typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword), false)),
127
+ ])
128
+ : names.length === 0
129
+ ? typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword)
130
+ : typescript_1.default.factory.createUnionTypeNode([
131
+ ...names.map(n => typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral(String(n)))),
132
+ ...(name === 'mesg_num'
133
+ ? [
134
+ typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral('definition')),
135
+ ]
136
+ : []),
137
+ typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword),
138
+ ]);
110
139
  const typeAlias = typescript_1.default.factory.createTypeAliasDeclaration([
111
140
  typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword),
112
- ], snakeToCamel(name), undefined, typescript_1.default.factory.createUnionTypeNode([...names.map(n => typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral(String(n)))), ...(name === 'mesg_num'
113
- ? [
114
- typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral('definition')),
115
- ]
116
- : [])]));
141
+ ], snakeToCamel(name), undefined, typeNode);
117
142
  nodes.push(typeAlias);
118
143
  });
119
144
  return nodes;
@@ -150,13 +175,6 @@ function generateUtilities() {
150
175
  ]),
151
176
  ]));
152
177
  nodes.push(unitType);
153
- const messageIndex = typescript_1.default.factory.createInterfaceDeclaration([typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword)], 'MessageIndex', undefined, undefined, [
154
- generateProperty('0', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword), false),
155
- generateProperty('value', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword), false),
156
- generateProperty('reserved', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword), false),
157
- generateProperty('selected', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword), false),
158
- ]);
159
- nodes.push(messageIndex);
160
178
  return nodes;
161
179
  }
162
180
  function generateAdditionalFields(msg) {
@@ -180,6 +198,7 @@ function generateAdditionalFields(msg) {
180
198
  developer_data_ids: 'parsed_developer_data_id',
181
199
  field_descriptions: 'parsed_field_description',
182
200
  sports: 'parsed_sport',
201
+ sets: 'parsed_set',
183
202
  splits: 'parsed_split',
184
203
  split_summaries: 'parsed_split_summary',
185
204
  };
@@ -211,6 +230,7 @@ function generateFitType() {
211
230
  tank_updates: 'parsed_tank_update',
212
231
  tank_summaries: 'parsed_tank_summary',
213
232
  jumps: 'parsed_jump',
233
+ sets: 'parsed_set',
214
234
  splits: 'parsed_split',
215
235
  split_summaries: 'parsed_split_summary',
216
236
  time_in_zone: 'parsed_time_in_zone',
@@ -227,31 +247,38 @@ function generateFitType() {
227
247
  activity: 'parsed_activity',
228
248
  zones_target: '?parsed_zones_target',
229
249
  };
250
+ const existingMessageNames = new Set([
251
+ ...Object.values(collectionProperties),
252
+ ...Object.values(referenceProperties),
253
+ ].map(name => name.replace('?', '').replace(/^parsed_/, '')));
254
+ const additionalReferenceProperties = Object.values(fit_js_1.FIT.messages)
255
+ .filter(message => !existingMessageNames.has(message.name))
256
+ .map(message => generateProperty(message.name, typescript_1.default.factory.createTypeReferenceNode(snakeToCamel(`parsed_${message.name}`))));
230
257
  return typescript_1.default.factory.createInterfaceDeclaration([typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword)], 'ParsedFit', undefined, undefined, [
231
258
  generateProperty('protocolVersion', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword)),
232
259
  generateProperty('profileVersion', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword)),
260
+ generateProperty('messages', typescript_1.default.factory.createTypeReferenceNode('ParsedMessages')),
233
261
  ...Object.keys(referenceProperties).map(prop => generateProperty(prop, typescript_1.default.factory.createTypeReferenceNode(snakeToCamel(referenceProperties[prop].replace('?', ''))), referenceProperties[prop].startsWith('?'))),
234
262
  ...Object.keys(collectionProperties).map(prop => generateArrayProperty(prop, collectionProperties[prop] === 'unknown'
235
263
  ? typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.UnknownKeyword)
236
264
  : typescript_1.default.factory.createTypeReferenceNode(snakeToCamel(collectionProperties[prop])))),
265
+ ...additionalReferenceProperties,
237
266
  ]);
238
267
  }
268
+ function generateParsedMessages(messages) {
269
+ return typescript_1.default.factory.createInterfaceDeclaration([typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword)], 'ParsedMessages', undefined, undefined, Object.values(messages).map(message => generateArrayProperty(message.name, typescript_1.default.factory.createTypeReferenceNode(snakeToCamel(`parsed_${message.name}`)))));
270
+ }
239
271
  function generateMessages(messages) {
240
272
  const nodes = [];
241
273
  Object.keys(messages).forEach((name) => {
242
274
  const msg = fit_js_1.FIT.messages[Number(name)];
243
- const usedFields = new Set();
244
275
  const messageType = typescript_1.default.factory.createInterfaceDeclaration([
245
276
  typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword),
246
277
  ], snakeToCamel(`parsed_${msg.name}`), undefined, undefined, [
247
- ...Object.keys(msg).filter(n => n !== 'name').reduce((acc, id) => {
278
+ ...Object.keys(msg).filter(n => n !== 'name').map((id) => {
248
279
  const def = msg[Number(id)];
249
- if (!usedFields.has(def.field)) {
250
- usedFields.add(def.field);
251
- acc.push(generateProperty(def.field, generateTypeFromField(def), !['start_time', 'timestamp'].includes(def.field)));
252
- }
253
- return acc;
254
- }, []),
280
+ return generateProperty(def.field, generateTypeFromField(def));
281
+ }),
255
282
  ...generateAdditionalFields(msg),
256
283
  ]);
257
284
  nodes.push(messageType);
@@ -271,6 +298,8 @@ function main() {
271
298
  nodes.push(...generateOptions(fit_js_1.FIT.options));
272
299
  nodes.push(...comment('parsed from Fit.messages'));
273
300
  nodes.push(...generateMessages(fit_js_1.FIT.messages));
301
+ nodes.push(...comment('all recognized messages, retained in file order'));
302
+ nodes.push(generateParsedMessages(fit_js_1.FIT.messages));
274
303
  nodes.push(...comment('the returned type after parsing a .fit file'));
275
304
  nodes.push(generateFitType());
276
305
  const nodesArray = typescript_1.default.factory.createNodeArray(nodes);
@@ -26,23 +26,23 @@ export default class FitParser {
26
26
  });
27
27
  }
28
28
  parse(content, callback) {
29
- var _a;
29
+ var _a, _b;
30
30
  const blob = content instanceof ArrayBuffer
31
31
  ? new Uint8Array(content)
32
32
  : new Uint8Array(content.buffer, content.byteOffset, content.byteLength);
33
33
  const dataView = new DataView(blob.buffer, blob.byteOffset, blob.byteLength);
34
34
  if (blob.length < 12) {
35
35
  callback('File to small to be a FIT file', undefined);
36
- if (!this.options.force) {
37
- return;
38
- }
36
+ return;
39
37
  }
40
38
  const headerLength = blob[0];
41
39
  if (headerLength !== 14 && headerLength !== 12) {
42
40
  callback('Incorrect header size', undefined);
43
- if (!this.options.force) {
44
- return;
45
- }
41
+ return;
42
+ }
43
+ if (blob.length < headerLength) {
44
+ callback('File to small to be a FIT file', undefined);
45
+ return;
46
46
  }
47
47
  let fileTypeString = '';
48
48
  for (let i = 8; i < 12; i++) {
@@ -50,29 +50,33 @@ export default class FitParser {
50
50
  }
51
51
  if (fileTypeString !== '.FIT') {
52
52
  callback('Missing \'.FIT\' in header', undefined);
53
- if (!this.options.force) {
54
- return;
55
- }
53
+ return;
56
54
  }
57
55
  if (headerLength === 14 && !this.options.force) {
58
56
  const crcHeader = blob[12] + (blob[13] << 8);
59
57
  const crcHeaderCalc = calculateCRC(blob, 0, 12);
60
- if (crcHeader !== crcHeaderCalc) {
61
- // callback('Header CRC mismatch', {});
62
- // TODO: fix Header CRC check
58
+ if (crcHeader !== 0 && crcHeader !== crcHeaderCalc) {
59
+ callback('Header CRC mismatch', undefined);
63
60
  return;
64
61
  }
65
62
  }
66
63
  const protocolVersion = blob[1];
67
64
  const profileVersion = blob[2] + (blob[3] << 8);
68
- const dataLength = blob[4] + (blob[5] << 8) + (blob[6] << 16) + (blob[7] << 24);
65
+ const dataLength = dataView.getUint32(4, true);
69
66
  const crcStart = dataLength + headerLength;
67
+ if (crcStart > blob.length) {
68
+ callback('File data exceeds input length', undefined);
69
+ return;
70
+ }
71
+ if (!this.options.force && crcStart + 2 > blob.length) {
72
+ callback('File CRC missing', undefined);
73
+ return;
74
+ }
70
75
  if (!this.options.force) {
71
76
  const crcFile = blob[crcStart] + (blob[crcStart + 1] << 8);
72
- const crcFileCalc = calculateCRC(blob, headerLength === 12 ? 0 : headerLength, crcStart);
77
+ const crcFileCalc = calculateCRC(blob, 0, crcStart);
73
78
  if (crcFile !== crcFileCalc) {
74
- // callback('File CRC mismatch', {});
75
- // TODO: fix File CRC check
79
+ callback('File CRC mismatch', undefined);
76
80
  return;
77
81
  }
78
82
  }
@@ -80,6 +84,7 @@ export default class FitParser {
80
84
  profileVersion,
81
85
  protocolVersion,
82
86
  };
87
+ const messages = {};
83
88
  let sessions = [];
84
89
  let laps = [];
85
90
  const records = [];
@@ -102,6 +107,7 @@ export default class FitParser {
102
107
  const tank_updates = [];
103
108
  const tank_summaries = [];
104
109
  const jumps = [];
110
+ const sets = [];
105
111
  const splits = [];
106
112
  const split_summaries = [];
107
113
  const time_in_zone = [];
@@ -110,14 +116,20 @@ export default class FitParser {
110
116
  let loopIndex = headerLength;
111
117
  const messageTypes = [];
112
118
  const developerFields = [];
119
+ const decoderState = {};
113
120
  const isModeCascade = this.options.mode === 'cascade';
114
121
  const isCascadeNeeded = isModeCascade || this.options.mode === 'both';
115
122
  let startDate;
116
123
  let lastStopTimestamp;
117
124
  let pausedTime = 0;
118
125
  while (loopIndex < crcStart) {
119
- const { nextIndex, messageType, message } = readRecord(blob, messageTypes, developerFields, loopIndex, this.options, startDate, pausedTime, dataView);
126
+ const { nextIndex, messageType, message } = readRecord(blob, messageTypes, developerFields, loopIndex, this.options, startDate, pausedTime, dataView, decoderState);
120
127
  loopIndex = nextIndex;
128
+ if (messageType !== ''
129
+ && messageType !== 'definition'
130
+ && message !== undefined) {
131
+ ((_a = messages[messageType]) !== null && _a !== void 0 ? _a : (messages[messageType] = [])).push(message);
132
+ }
121
133
  switch (messageType) {
122
134
  case 'lap':
123
135
  laps.push(message);
@@ -151,8 +163,10 @@ export default class FitParser {
151
163
  case 'record':
152
164
  if (!startDate) {
153
165
  startDate = message.timestamp;
154
- message.elapsed_time = 0;
155
- message.timer_time = 0;
166
+ if (this.options.elapsedRecordField) {
167
+ message.elapsed_time = 0;
168
+ message.timer_time = 0;
169
+ }
156
170
  }
157
171
  records.push(message);
158
172
  break;
@@ -205,6 +219,9 @@ export default class FitParser {
205
219
  case 'jump':
206
220
  jumps.push(message);
207
221
  break;
222
+ case 'set':
223
+ sets.push(message);
224
+ break;
208
225
  case 'split':
209
226
  splits.push(message);
210
227
  break;
@@ -245,15 +262,17 @@ export default class FitParser {
245
262
  fitObj.time_in_zone = time_in_zone;
246
263
  fitObj.activity_metrics = activity_metrics;
247
264
  fitObj.user_metrics = user_metrics;
265
+ fitObj.messages = messages;
248
266
  if (isCascadeNeeded) {
249
267
  laps = mapDataIntoLap(laps, 'records', records);
250
268
  laps = mapDataIntoLap(laps, 'lengths', lengths);
251
269
  sessions = mapDataIntoSession(sessions, laps);
252
- fitObj.activity = Object.assign(Object.assign({}, ((_a = fitObj.activity) !== null && _a !== void 0 ? _a : {})), { // ugly but we assume the activity was parsed correctly with all other members correctly
270
+ 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
253
271
  sessions,
254
272
  events,
255
273
  hrv,
256
274
  device_infos, developer_data_ids: applications, field_descriptions: fieldDescriptions, sports,
275
+ sets,
257
276
  splits,
258
277
  split_summaries });
259
278
  }
@@ -267,6 +286,7 @@ export default class FitParser {
267
286
  fitObj.developer_data_ids = applications;
268
287
  fitObj.field_descriptions = fieldDescriptions;
269
288
  fitObj.hrv = hrv;
289
+ fitObj.sets = sets;
270
290
  }
271
291
  callback(undefined, fitObj);
272
292
  }
package/dist/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: MesgNum;
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
- [messageId: number]: Message;
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;