fit-file-parser 4.0.2 → 4.1.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.
@@ -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[];
@@ -39,6 +39,9 @@ export function generateArrayProperty(name, type, optional = true) {
39
39
  return generateProperty(name, ts.factory.createArrayTypeNode(type), optional);
40
40
  }
41
41
  export function generateTypeFromField(def) {
42
+ if (def.array === true) {
43
+ return ts.factory.createArrayTypeNode(generateTypeFromField(Object.assign(Object.assign({}, def), { array: false })));
44
+ }
42
45
  switch (def.type) {
43
46
  case 'uint32_array':
44
47
  case 'uint16_array':
@@ -48,6 +51,8 @@ export function generateTypeFromField(def) {
48
51
  case 'sint8_array':
49
52
  case 'byte_array':
50
53
  return ts.factory.createArrayTypeNode(ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword));
54
+ case 'exercise_category_array':
55
+ return ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode('ExerciseCategory'));
51
56
  case 'bool':
52
57
  return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
53
58
  case 'date_time':
@@ -89,11 +94,13 @@ export function generateTypes(types) {
89
94
  const names = Object.values(type);
90
95
  const typeAlias = ts.factory.createTypeAliasDeclaration([
91
96
  ts.factory.createModifier(ts.SyntaxKind.ExportKeyword),
92
- ], snakeToCamel(name), undefined, ts.factory.createUnionTypeNode([...names.map(n => ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(String(n)))), ...(name === 'mesg_num'
93
- ? [
94
- ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('definition')),
95
- ]
96
- : [])]));
97
+ ], snakeToCamel(name), undefined, names.length === 0
98
+ ? ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)
99
+ : ts.factory.createUnionTypeNode([...names.map(n => ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(String(n)))), ...(name === 'mesg_num'
100
+ ? [
101
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('definition')),
102
+ ]
103
+ : [])]));
97
104
  nodes.push(typeAlias);
98
105
  });
99
106
  return nodes;
@@ -160,6 +167,7 @@ function generateAdditionalFields(msg) {
160
167
  developer_data_ids: 'parsed_developer_data_id',
161
168
  field_descriptions: 'parsed_field_description',
162
169
  sports: 'parsed_sport',
170
+ sets: 'parsed_set',
163
171
  splits: 'parsed_split',
164
172
  split_summaries: 'parsed_split_summary',
165
173
  };
@@ -191,6 +199,7 @@ export function generateFitType() {
191
199
  tank_updates: 'parsed_tank_update',
192
200
  tank_summaries: 'parsed_tank_summary',
193
201
  jumps: 'parsed_jump',
202
+ sets: 'parsed_set',
194
203
  splits: 'parsed_split',
195
204
  split_summaries: 'parsed_split_summary',
196
205
  time_in_zone: 'parsed_time_in_zone',
@@ -207,15 +216,27 @@ export function generateFitType() {
207
216
  activity: 'parsed_activity',
208
217
  zones_target: '?parsed_zones_target',
209
218
  };
219
+ const existingMessageNames = new Set([
220
+ ...Object.values(collectionProperties),
221
+ ...Object.values(referenceProperties),
222
+ ].map(name => name.replace('?', '').replace(/^parsed_/, '')));
223
+ const additionalReferenceProperties = Object.values(FIT.messages)
224
+ .filter(message => !existingMessageNames.has(message.name))
225
+ .map(message => generateProperty(message.name, ts.factory.createTypeReferenceNode(snakeToCamel(`parsed_${message.name}`))));
210
226
  return ts.factory.createInterfaceDeclaration([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], 'ParsedFit', undefined, undefined, [
211
227
  generateProperty('protocolVersion', ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),
212
228
  generateProperty('profileVersion', ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),
229
+ generateProperty('messages', ts.factory.createTypeReferenceNode('ParsedMessages')),
213
230
  ...Object.keys(referenceProperties).map(prop => generateProperty(prop, ts.factory.createTypeReferenceNode(snakeToCamel(referenceProperties[prop].replace('?', ''))), referenceProperties[prop].startsWith('?'))),
214
231
  ...Object.keys(collectionProperties).map(prop => generateArrayProperty(prop, collectionProperties[prop] === 'unknown'
215
232
  ? ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)
216
233
  : ts.factory.createTypeReferenceNode(snakeToCamel(collectionProperties[prop])))),
234
+ ...additionalReferenceProperties,
217
235
  ]);
218
236
  }
237
+ export function generateParsedMessages(messages) {
238
+ return ts.factory.createInterfaceDeclaration([ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], 'ParsedMessages', undefined, undefined, Object.values(messages).map(message => generateArrayProperty(message.name, ts.factory.createTypeReferenceNode(snakeToCamel(`parsed_${message.name}`)))));
239
+ }
219
240
  export function generateMessages(messages) {
220
241
  const nodes = [];
221
242
  Object.keys(messages).forEach((name) => {
@@ -225,11 +246,15 @@ export function generateMessages(messages) {
225
246
  ts.factory.createModifier(ts.SyntaxKind.ExportKeyword),
226
247
  ], snakeToCamel(`parsed_${msg.name}`), undefined, undefined, [
227
248
  ...Object.keys(msg).filter(n => n !== 'name').reduce((acc, id) => {
249
+ var _a;
228
250
  const def = msg[Number(id)];
229
- if (!usedFields.has(def.field)) {
230
- usedFields.add(def.field);
231
- acc.push(generateProperty(def.field, generateTypeFromField(def), !['start_time', 'timestamp'].includes(def.field)));
232
- }
251
+ const definitions = [def, ...((_a = def.aliases) !== null && _a !== void 0 ? _a : [])];
252
+ definitions.forEach((definition) => {
253
+ if (!usedFields.has(definition.field)) {
254
+ usedFields.add(definition.field);
255
+ acc.push(generateProperty(definition.field, generateTypeFromField(definition), !['start_time', 'timestamp'].includes(definition.field)));
256
+ }
257
+ });
233
258
  return acc;
234
259
  }, []),
235
260
  ...generateAdditionalFields(msg),
@@ -251,6 +276,8 @@ export function main() {
251
276
  nodes.push(...generateOptions(FIT.options));
252
277
  nodes.push(...comment('parsed from Fit.messages'));
253
278
  nodes.push(...generateMessages(FIT.messages));
279
+ nodes.push(...comment('all recognized messages, retained in file order'));
280
+ nodes.push(generateParsedMessages(FIT.messages));
254
281
  nodes.push(...comment('the returned type after parsing a .fit file'));
255
282
  nodes.push(generateFitType());
256
283
  const nodesArray = ts.factory.createNodeArray(nodes);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fit-file-parser",
3
3
  "type": "module",
4
- "version": "4.0.2",
4
+ "version": "4.1.0",
5
5
  "private": false,
6
6
  "description": "Parse your .FIT files easily, directly from JS (Garmin, Polar, Suunto)",
7
7
  "author": {
@@ -54,10 +54,13 @@
54
54
  }
55
55
  ],
56
56
  "scripts": {
57
- "dev": "tsx --watch --watch-path=src/type_generator.ts --watch-path=src/fit.ts codegen/codegen.ts",
58
- "codegen": "tsx codegen/codegen.ts",
59
- "codegen:check": "tsx codegen/codegen.ts --check",
60
- "check": "npm run codegen:check && npm run lint && npm run type-check && npm test -- --run && npm run build",
57
+ "dev": "tsx --watch --watch-path=src/type_generator.ts --watch-path=src/fit.ts --watch-path=src/garmin_profile.generated.ts codegen/codegen.ts",
58
+ "codegen:profile": "tsx codegen/garmin-profile.ts",
59
+ "codegen": "npm run codegen:profile && tsx codegen/codegen.ts",
60
+ "codegen:check": "tsx codegen/garmin-profile.ts --check && tsx codegen/codegen.ts --check",
61
+ "profile:audit": "npm run codegen:check && tsx scripts/profile-audit.ts",
62
+ "corpus:check": "tsx scripts/corpus-check.ts",
63
+ "check": "npm run profile:audit && npm run lint && npm run type-check && npm test -- --run && npm run build",
61
64
  "example-output": "node examples/parse.js examples/example.fit > examples/output-example.json",
62
65
  "example-output-tank": "node examples/parse.js examples/example-diving.fit > examples/output-diving.json",
63
66
  "example-output-summary-first": "node examples/parse.js examples/triathlon_summary_first.fit > examples/output_summary_first.json",
@@ -77,7 +80,7 @@
77
80
  },
78
81
  "devDependencies": {
79
82
  "@antfu/eslint-config": "^6.2.0",
80
- "@garmin/fitsdk": "^21.208.0",
83
+ "@garmin/fitsdk": "21.208.0",
81
84
  "@types/node": "^24.10.1",
82
85
  "@vitest/coverage-v8": "^4.0.13",
83
86
  "eslint": "^9.10.0",