fit-file-parser 4.1.0 → 5.0.2

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;
@@ -59,9 +59,15 @@ function generateProperty(name, type, optional = true) {
59
59
  function generateArrayProperty(name, type, optional = true) {
60
60
  return generateProperty(name, typescript_1.default.factory.createArrayTypeNode(type), optional);
61
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
+ }
62
68
  function generateTypeFromField(def) {
63
69
  if (def.array === true) {
64
- return typescript_1.default.factory.createArrayTypeNode(generateTypeFromField(Object.assign(Object.assign({}, def), { array: false })));
70
+ return generateNullableArrayType(generateTypeFromField(Object.assign(Object.assign({}, def), { array: false })));
65
71
  }
66
72
  switch (def.type) {
67
73
  case 'uint32_array':
@@ -71,14 +77,15 @@ function generateTypeFromField(def) {
71
77
  case 'sint16_array':
72
78
  case 'sint8_array':
73
79
  case 'byte_array':
74
- return typescript_1.default.factory.createArrayTypeNode(typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword));
80
+ return generateNullableArrayType(typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword));
75
81
  case 'exercise_category_array':
76
- return typescript_1.default.factory.createArrayTypeNode(typescript_1.default.factory.createTypeReferenceNode('ExerciseCategory'));
77
- case 'bool':
78
- return typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword);
82
+ return generateNullableArrayType(typescript_1.default.factory.createTypeReferenceNode('ExerciseCategory'));
79
83
  case 'date_time':
84
+ case 'local_date_time':
85
+ return typescript_1.default.factory.createTypeReferenceNode('Date');
80
86
  case 'string':
81
87
  return typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.StringKeyword);
88
+ case 'bool':
82
89
  case 'uint32':
83
90
  case 'uint64':
84
91
  case 'uint16':
@@ -108,20 +115,30 @@ function generateTypes(types) {
108
115
  const nodes = [];
109
116
  const typeNames = Object.keys(types);
110
117
  typeNames.forEach((name) => {
111
- if (name === 'message_index') {
112
- return;
113
- }
114
118
  const type = types[name];
115
- 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
+ ]);
116
139
  const typeAlias = typescript_1.default.factory.createTypeAliasDeclaration([
117
140
  typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword),
118
- ], snakeToCamel(name), undefined, names.length === 0
119
- ? typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword)
120
- : typescript_1.default.factory.createUnionTypeNode([...names.map(n => typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral(String(n)))), ...(name === 'mesg_num'
121
- ? [
122
- typescript_1.default.factory.createLiteralTypeNode(typescript_1.default.factory.createStringLiteral('definition')),
123
- ]
124
- : [])]));
141
+ ], snakeToCamel(name), undefined, typeNode);
125
142
  nodes.push(typeAlias);
126
143
  });
127
144
  return nodes;
@@ -158,13 +175,6 @@ function generateUtilities() {
158
175
  ]),
159
176
  ]));
160
177
  nodes.push(unitType);
161
- const messageIndex = typescript_1.default.factory.createInterfaceDeclaration([typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword)], 'MessageIndex', undefined, undefined, [
162
- generateProperty('0', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword), false),
163
- generateProperty('value', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.NumberKeyword), false),
164
- generateProperty('reserved', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword), false),
165
- generateProperty('selected', typescript_1.default.factory.createKeywordTypeNode(typescript_1.default.SyntaxKind.BooleanKeyword), false),
166
- ]);
167
- nodes.push(messageIndex);
168
178
  return nodes;
169
179
  }
170
180
  function generateAdditionalFields(msg) {
@@ -262,22 +272,13 @@ function generateMessages(messages) {
262
272
  const nodes = [];
263
273
  Object.keys(messages).forEach((name) => {
264
274
  const msg = fit_js_1.FIT.messages[Number(name)];
265
- const usedFields = new Set();
266
275
  const messageType = typescript_1.default.factory.createInterfaceDeclaration([
267
276
  typescript_1.default.factory.createModifier(typescript_1.default.SyntaxKind.ExportKeyword),
268
277
  ], snakeToCamel(`parsed_${msg.name}`), undefined, undefined, [
269
- ...Object.keys(msg).filter(n => n !== 'name').reduce((acc, id) => {
270
- var _a;
278
+ ...Object.keys(msg).filter(n => n !== 'name').map((id) => {
271
279
  const def = msg[Number(id)];
272
- const definitions = [def, ...((_a = def.aliases) !== null && _a !== void 0 ? _a : [])];
273
- definitions.forEach((definition) => {
274
- if (!usedFields.has(definition.field)) {
275
- usedFields.add(definition.field);
276
- acc.push(generateProperty(definition.field, generateTypeFromField(definition), !['start_time', 'timestamp'].includes(definition.field)));
277
- }
278
- });
279
- return acc;
280
- }, []),
280
+ return generateProperty(def.field, generateTypeFromField(def));
281
+ }),
281
282
  ...generateAdditionalFields(msg),
282
283
  ]);
283
284
  nodes.push(messageType);
@@ -163,8 +163,10 @@ export default class FitParser {
163
163
  case 'record':
164
164
  if (!startDate) {
165
165
  startDate = message.timestamp;
166
- message.elapsed_time = 0;
167
- message.timer_time = 0;
166
+ if (this.options.elapsedRecordField) {
167
+ message.elapsed_time = 0;
168
+ message.timer_time = 0;
169
+ }
168
170
  }
169
171
  records.push(message);
170
172
  break;
package/dist/fit.d.ts CHANGED
@@ -1,4 +1,5 @@
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;
4
5
  rawType?: string | number;
@@ -12,10 +13,10 @@ export interface FieldDefinition {
12
13
  dataType: string;
13
14
  scale?: number | null;
14
15
  offset?: number;
16
+ units?: string;
15
17
  requiresBoundedDataView?: boolean;
16
18
  developerDataIndex?: number;
17
19
  isDeveloperField?: boolean;
18
- aliases?: MessageObject[];
19
20
  }
20
21
  export interface MessageObject {
21
22
  field: string;
@@ -25,20 +26,21 @@ export interface MessageObject {
25
26
  scale: number | null;
26
27
  offset: number;
27
28
  units: string;
28
- aliases?: MessageObject[];
29
29
  }
30
30
  export interface Message {
31
- name: MesgNum;
31
+ name: MessageName;
32
32
  [fieldId: number]: MessageObject;
33
33
  }
34
34
  export interface FitType {
35
35
  scConst: number;
36
36
  options: FitOptions;
37
- messages: {
38
- [messageId: number]: Message;
39
- };
40
- types: {
41
- [typeName: string]: Record<number, string | number>;
42
- };
37
+ messages: Record<number, Message>;
38
+ types: Record<string, Record<number, string | number>>;
43
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>>;
44
46
  export declare const FIT: FitType;