fit-file-parser 3.0.2 → 3.1.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Change Log
2
2
 
3
+ ## 3.1.0
4
+
5
+ - Add the public `FitEncoder` API for writing FIT headers, definitions, data messages, and CRCs.
6
+ - Preserve the `course.sub_sport` field while parsing FIT course files.
7
+
3
8
  <a name="1.5.4"></a>
4
9
 
5
10
  ## [1.5.4](https://github.com/jimmykane/fit-parser/compare/v1.0.0...v1.5.3) (2019-03-01)
package/README.md CHANGED
@@ -57,6 +57,31 @@ const buffer = await fs.readFile('./example.fit')
57
57
  const fitObject = await fitParser.parseAsync(buffer)
58
58
  ```
59
59
 
60
+ ## Encoding
61
+
62
+ `FitEncoder` writes FIT headers, message definitions, data messages, and CRCs.
63
+ It is profile-agnostic: callers provide profile field identifiers and values in
64
+ their raw FIT representation (including any scale or offset). Scalar 64-bit
65
+ values use `bigint`; strings and numeric arrays use exact-size raw
66
+ `Uint8Array` values.
67
+
68
+ ```javascript
69
+ import { FitBaseType, FitEncoder } from 'fit-file-parser'
70
+
71
+ const encoder = new FitEncoder()
72
+ encoder.writeMessage(0, [
73
+ { number: 0, size: 1, baseType: FitBaseType.Enum, value: 6 }, // FileId.type = course
74
+ { number: 4, size: 4, baseType: FitBaseType.Uint32, value: FitEncoder.toFitTimestamp(new Date()) },
75
+ ])
76
+
77
+ const fitBytes = encoder.close()
78
+ ```
79
+
80
+ Use a distinct local message number (the optional third `writeMessage`
81
+ argument) for each recurring message shape to avoid redundant definitions.
82
+ The encoder validates field definitions and numeric ranges before writing, so
83
+ an exception never leaves a partial message in the output.
84
+
60
85
  ## Development
61
86
 
62
87
  To build the project, run:
package/dist/binary.js CHANGED
@@ -254,6 +254,7 @@ function applyOptions(data, field, options, fields) {
254
254
  case 'gps_accuracy':
255
255
  return convertTo(data, 'lengthUnits', options.lengthUnit);
256
256
  case 'temperature':
257
+ case 'min_temperature':
257
258
  case 'avg_temperature':
258
259
  case 'max_temperature':
259
260
  return convertTo(data, 'temperatureUnits', options.temperatureUnit);
@@ -260,6 +260,7 @@ function applyOptions(data, field, options, fields) {
260
260
  case 'gps_accuracy':
261
261
  return convertTo(data, 'lengthUnits', options.lengthUnit);
262
262
  case 'temperature':
263
+ case 'min_temperature':
263
264
  case 'avg_temperature':
264
265
  case 'max_temperature':
265
266
  return convertTo(data, 'temperatureUnits', options.temperatureUnit);
@@ -0,0 +1,68 @@
1
+ /** FIT definition base-type bytes, including the endian flag where required. */
2
+ export declare enum FitBaseType {
3
+ Enum = 0,
4
+ Sint8 = 1,
5
+ Uint8 = 2,
6
+ String = 7,
7
+ Uint8z = 10,
8
+ Byte = 13,
9
+ Sint16 = 131,
10
+ Uint16 = 132,
11
+ Sint32 = 133,
12
+ Uint32 = 134,
13
+ Float32 = 136,
14
+ Float64 = 137,
15
+ Uint16z = 139,
16
+ Uint32z = 140,
17
+ Sint64 = 142,
18
+ Uint64 = 143,
19
+ Uint64z = 144
20
+ }
21
+ export interface FitEncoderField {
22
+ number: number;
23
+ size: number;
24
+ baseType: FitBaseType | number;
25
+ value: number | bigint | Uint8Array;
26
+ }
27
+ export interface FitEncoderOptions {
28
+ protocolVersion?: number;
29
+ profileVersion?: number;
30
+ }
31
+ /**
32
+ * A generic FIT binary encoder. Callers supply profile-specific field
33
+ * definitions and already-scaled field values. Numeric arrays, strings, and
34
+ * variable-length field values are supplied as raw `Uint8Array` values.
35
+ */
36
+ export declare class FitEncoder {
37
+ private readonly data;
38
+ private readonly activeDefinitions;
39
+ private readonly protocolVersion;
40
+ private readonly profileVersion;
41
+ constructor(options?: FitEncoderOptions);
42
+ writeMessage(globalMessageNumber: number, fields: FitEncoderField[], localMessageNumber?: number): this;
43
+ close(): Uint8Array;
44
+ static string(value: string): Uint8Array;
45
+ static toFitTimestamp(date: Date): number;
46
+ static calculateCRC(bytes: ArrayLike<number>): number;
47
+ private validateMessage;
48
+ private validateField;
49
+ private assertNumberInRange;
50
+ private assertBigIntInRange;
51
+ private getUnsignedMaximum;
52
+ private getFieldDefinition;
53
+ private writeDefinition;
54
+ private writeFieldValue;
55
+ private getBaseTypeSize;
56
+ private isSupportedBaseType;
57
+ private writeUInt8;
58
+ private writeUInt16;
59
+ private writeInt8;
60
+ private writeInt16;
61
+ private writeUInt32;
62
+ private writeInt32;
63
+ private writeUInt64;
64
+ private writeInt64;
65
+ private writeFloat32;
66
+ private writeFloat64;
67
+ private static assertIntegerInRange;
68
+ }
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FitEncoder = exports.FitBaseType = void 0;
4
+ const FIT_HEADER_SIZE = 14;
5
+ const FIT_EPOCH_MS = 631065600000;
6
+ const UINT8_MAX = 0xFF;
7
+ const UINT16_MAX = 0xFFFF;
8
+ const UINT32_MAX = 0xFFFFFFFF;
9
+ const BIGINT_ZERO = BigInt(0);
10
+ const BIGINT_EIGHT = BigInt(8);
11
+ const BIGINT_BYTE_MASK = BigInt(0xFF);
12
+ const UINT64_MAX = BigInt('18446744073709551615');
13
+ const UINT64_MODULUS = BigInt('18446744073709551616');
14
+ const SINT8_MIN = -0x80;
15
+ const SINT8_MAX = 0x7F;
16
+ const SINT16_MIN = -0x8000;
17
+ const SINT16_MAX = 0x7FFF;
18
+ const SINT32_MIN = -0x80000000;
19
+ const SINT32_MAX = 0x7FFFFFFF;
20
+ const SINT64_MIN = BigInt('-9223372036854775808');
21
+ const SINT64_MAX = BigInt('9223372036854775807');
22
+ /** FIT definition base-type bytes, including the endian flag where required. */
23
+ var FitBaseType;
24
+ (function (FitBaseType) {
25
+ FitBaseType[FitBaseType["Enum"] = 0] = "Enum";
26
+ FitBaseType[FitBaseType["Sint8"] = 1] = "Sint8";
27
+ FitBaseType[FitBaseType["Uint8"] = 2] = "Uint8";
28
+ FitBaseType[FitBaseType["String"] = 7] = "String";
29
+ FitBaseType[FitBaseType["Uint8z"] = 10] = "Uint8z";
30
+ FitBaseType[FitBaseType["Byte"] = 13] = "Byte";
31
+ FitBaseType[FitBaseType["Sint16"] = 131] = "Sint16";
32
+ FitBaseType[FitBaseType["Uint16"] = 132] = "Uint16";
33
+ FitBaseType[FitBaseType["Sint32"] = 133] = "Sint32";
34
+ FitBaseType[FitBaseType["Uint32"] = 134] = "Uint32";
35
+ FitBaseType[FitBaseType["Float32"] = 136] = "Float32";
36
+ FitBaseType[FitBaseType["Float64"] = 137] = "Float64";
37
+ FitBaseType[FitBaseType["Uint16z"] = 139] = "Uint16z";
38
+ FitBaseType[FitBaseType["Uint32z"] = 140] = "Uint32z";
39
+ FitBaseType[FitBaseType["Sint64"] = 142] = "Sint64";
40
+ FitBaseType[FitBaseType["Uint64"] = 143] = "Uint64";
41
+ FitBaseType[FitBaseType["Uint64z"] = 144] = "Uint64z";
42
+ })(FitBaseType || (exports.FitBaseType = FitBaseType = {}));
43
+ /**
44
+ * A generic FIT binary encoder. Callers supply profile-specific field
45
+ * definitions and already-scaled field values. Numeric arrays, strings, and
46
+ * variable-length field values are supplied as raw `Uint8Array` values.
47
+ */
48
+ class FitEncoder {
49
+ constructor(options = {}) {
50
+ var _a, _b;
51
+ this.data = [];
52
+ this.activeDefinitions = new Map();
53
+ this.protocolVersion = FitEncoder.assertIntegerInRange((_a = options.protocolVersion) !== null && _a !== void 0 ? _a : 2, 0, UINT8_MAX, 'FIT protocol version');
54
+ this.profileVersion = FitEncoder.assertIntegerInRange((_b = options.profileVersion) !== null && _b !== void 0 ? _b : 21188, 0, UINT16_MAX, 'FIT profile version');
55
+ }
56
+ writeMessage(globalMessageNumber, fields, localMessageNumber = 0) {
57
+ this.validateMessage(globalMessageNumber, fields, localMessageNumber);
58
+ const definitionSignature = JSON.stringify({
59
+ globalMessageNumber,
60
+ fields: fields.map(field => this.getFieldDefinition(field)),
61
+ });
62
+ if (this.activeDefinitions.get(localMessageNumber) !== definitionSignature) {
63
+ this.writeDefinition(localMessageNumber, globalMessageNumber, fields);
64
+ this.activeDefinitions.set(localMessageNumber, definitionSignature);
65
+ }
66
+ this.writeUInt8(localMessageNumber);
67
+ fields.forEach(field => this.writeFieldValue(field));
68
+ return this;
69
+ }
70
+ close() {
71
+ if (this.data.length > UINT32_MAX) {
72
+ throw new RangeError('FIT data section cannot exceed 4294967295 bytes');
73
+ }
74
+ const header = [
75
+ FIT_HEADER_SIZE,
76
+ this.protocolVersion,
77
+ this.profileVersion & UINT8_MAX,
78
+ (this.profileVersion >>> 8) & UINT8_MAX,
79
+ this.data.length & UINT8_MAX,
80
+ (this.data.length >>> 8) & UINT8_MAX,
81
+ (this.data.length >>> 16) & UINT8_MAX,
82
+ (this.data.length >>> 24) & UINT8_MAX,
83
+ 0x2E,
84
+ 0x46,
85
+ 0x49,
86
+ 0x54,
87
+ ];
88
+ const headerCRC = FitEncoder.calculateCRC(header);
89
+ const output = header.concat([headerCRC & UINT8_MAX, (headerCRC >>> 8) & UINT8_MAX], this.data);
90
+ const fileCRC = FitEncoder.calculateCRC(output);
91
+ output.push(fileCRC & UINT8_MAX, (fileCRC >>> 8) & UINT8_MAX);
92
+ return new Uint8Array(output);
93
+ }
94
+ static string(value) {
95
+ const bytes = new TextEncoder().encode(value);
96
+ if (bytes.length > UINT8_MAX - 1) {
97
+ throw new RangeError('FIT string fields can contain at most 254 UTF-8 bytes plus the null terminator');
98
+ }
99
+ const output = new Uint8Array(bytes.length + 1);
100
+ output.set(bytes);
101
+ return output;
102
+ }
103
+ static toFitTimestamp(date) {
104
+ if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
105
+ throw new TypeError('FIT timestamp requires a valid Date');
106
+ }
107
+ const timestamp = Math.floor((date.getTime() - FIT_EPOCH_MS) / 1000);
108
+ return FitEncoder.assertIntegerInRange(timestamp, 0, UINT32_MAX, 'FIT timestamp');
109
+ }
110
+ static calculateCRC(bytes) {
111
+ let crc = 0;
112
+ for (let index = 0; index < bytes.length; index++) {
113
+ let value = crc ^ bytes[index];
114
+ for (let bit = 0; bit < 8; bit++) {
115
+ value = value & 1 ? (value >>> 1) ^ 0xA001 : value >>> 1;
116
+ }
117
+ crc = value;
118
+ }
119
+ return crc;
120
+ }
121
+ validateMessage(globalMessageNumber, fields, localMessageNumber) {
122
+ FitEncoder.assertIntegerInRange(globalMessageNumber, 0, UINT16_MAX, 'FIT global message number');
123
+ FitEncoder.assertIntegerInRange(localMessageNumber, 0, 0x0F, 'FIT local message number');
124
+ if (!Array.isArray(fields) || fields.length > UINT8_MAX) {
125
+ throw new RangeError('FIT message definitions support between 0 and 255 fields');
126
+ }
127
+ fields.forEach(field => this.validateField(field));
128
+ }
129
+ validateField(field) {
130
+ if (!field || typeof field !== 'object') {
131
+ throw new TypeError('FIT field definitions must be objects');
132
+ }
133
+ FitEncoder.assertIntegerInRange(field.number, 0, UINT8_MAX, 'FIT field number');
134
+ FitEncoder.assertIntegerInRange(field.size, 1, UINT8_MAX, 'FIT field size');
135
+ FitEncoder.assertIntegerInRange(field.baseType, 0, UINT8_MAX, 'FIT base type');
136
+ if (!this.isSupportedBaseType(field.baseType)) {
137
+ throw new RangeError(`Unsupported FIT base type ${field.baseType}`);
138
+ }
139
+ if (field.value instanceof Uint8Array) {
140
+ if (field.value.length !== field.size) {
141
+ throw new RangeError(`FIT field ${field.number} expected ${field.size} bytes, received ${field.value.length}`);
142
+ }
143
+ const baseTypeSize = this.getBaseTypeSize(field.baseType);
144
+ if (baseTypeSize && field.size % baseTypeSize !== 0) {
145
+ throw new RangeError(`FIT field ${field.number} size must be a multiple of ${baseTypeSize}`);
146
+ }
147
+ return;
148
+ }
149
+ const baseTypeSize = this.getBaseTypeSize(field.baseType);
150
+ if (baseTypeSize === undefined || field.size !== baseTypeSize) {
151
+ throw new RangeError(`FIT field ${field.number} has an invalid size for base type ${field.baseType}`);
152
+ }
153
+ switch (field.baseType) {
154
+ case FitBaseType.Sint64:
155
+ this.assertBigIntInRange(field.value, SINT64_MIN, SINT64_MAX, field.number);
156
+ return;
157
+ case FitBaseType.Uint64:
158
+ case FitBaseType.Uint64z:
159
+ this.assertBigIntInRange(field.value, BIGINT_ZERO, UINT64_MAX, field.number);
160
+ return;
161
+ case FitBaseType.Sint8:
162
+ this.assertNumberInRange(field.value, SINT8_MIN, SINT8_MAX, field.number);
163
+ return;
164
+ case FitBaseType.Sint16:
165
+ this.assertNumberInRange(field.value, SINT16_MIN, SINT16_MAX, field.number);
166
+ return;
167
+ case FitBaseType.Sint32:
168
+ this.assertNumberInRange(field.value, SINT32_MIN, SINT32_MAX, field.number);
169
+ return;
170
+ case FitBaseType.Float32:
171
+ if (typeof field.value !== 'number' || !Number.isFinite(field.value)) {
172
+ throw new RangeError(`FIT field ${field.number} requires a finite numeric value`);
173
+ }
174
+ if (!Number.isFinite(Math.fround(field.value))) {
175
+ throw new RangeError(`FIT field ${field.number} must be representable as a finite float32`);
176
+ }
177
+ return;
178
+ case FitBaseType.Float64:
179
+ if (typeof field.value !== 'number' || !Number.isFinite(field.value)) {
180
+ throw new RangeError(`FIT field ${field.number} requires a finite numeric value`);
181
+ }
182
+ return;
183
+ default:
184
+ this.assertNumberInRange(field.value, 0, this.getUnsignedMaximum(field.baseType), field.number);
185
+ }
186
+ }
187
+ assertNumberInRange(value, minimum, maximum, fieldNumber) {
188
+ if (typeof value !== 'number'
189
+ || !Number.isInteger(value)
190
+ || value < minimum
191
+ || value > maximum) {
192
+ throw new RangeError(`FIT field ${fieldNumber} must be an integer between ${minimum} and ${maximum}`);
193
+ }
194
+ }
195
+ assertBigIntInRange(value, minimum, maximum, fieldNumber) {
196
+ if (typeof value !== 'bigint' || value < minimum || value > maximum) {
197
+ throw new RangeError(`FIT field ${fieldNumber} must be a bigint between ${minimum} and ${maximum}`);
198
+ }
199
+ }
200
+ getUnsignedMaximum(baseType) {
201
+ switch (baseType) {
202
+ case FitBaseType.Enum:
203
+ case FitBaseType.Uint8:
204
+ case FitBaseType.Uint8z:
205
+ case FitBaseType.Byte:
206
+ return UINT8_MAX;
207
+ case FitBaseType.Uint16:
208
+ case FitBaseType.Uint16z:
209
+ return UINT16_MAX;
210
+ case FitBaseType.Uint32:
211
+ case FitBaseType.Uint32z:
212
+ return UINT32_MAX;
213
+ default:
214
+ throw new RangeError(`Unsupported FIT base type ${baseType}`);
215
+ }
216
+ }
217
+ getFieldDefinition(field) {
218
+ return { number: field.number, size: field.size, baseType: field.baseType };
219
+ }
220
+ writeDefinition(localMessageNumber, globalMessageNumber, fields) {
221
+ this.writeUInt8(0x40 | localMessageNumber);
222
+ this.writeUInt8(0);
223
+ this.writeUInt8(0);
224
+ this.writeUInt16(globalMessageNumber);
225
+ this.writeUInt8(fields.length);
226
+ fields.forEach((field) => {
227
+ this.writeUInt8(field.number);
228
+ this.writeUInt8(field.size);
229
+ this.writeUInt8(field.baseType);
230
+ });
231
+ }
232
+ writeFieldValue(field) {
233
+ if (field.value instanceof Uint8Array) {
234
+ field.value.forEach(value => this.writeUInt8(value));
235
+ return;
236
+ }
237
+ switch (field.baseType) {
238
+ case FitBaseType.Enum:
239
+ case FitBaseType.Uint8:
240
+ case FitBaseType.Uint8z:
241
+ case FitBaseType.Byte:
242
+ this.writeUInt8(field.value);
243
+ return;
244
+ case FitBaseType.Sint8:
245
+ this.writeInt8(field.value);
246
+ return;
247
+ case FitBaseType.Uint16:
248
+ case FitBaseType.Uint16z:
249
+ this.writeUInt16(field.value);
250
+ return;
251
+ case FitBaseType.Sint16:
252
+ this.writeInt16(field.value);
253
+ return;
254
+ case FitBaseType.Uint32:
255
+ case FitBaseType.Uint32z:
256
+ this.writeUInt32(field.value);
257
+ return;
258
+ case FitBaseType.Sint32:
259
+ this.writeInt32(field.value);
260
+ return;
261
+ case FitBaseType.Float32:
262
+ this.writeFloat32(field.value);
263
+ return;
264
+ case FitBaseType.Float64:
265
+ this.writeFloat64(field.value);
266
+ return;
267
+ case FitBaseType.Sint64:
268
+ this.writeInt64(field.value);
269
+ return;
270
+ case FitBaseType.Uint64:
271
+ case FitBaseType.Uint64z:
272
+ this.writeUInt64(field.value);
273
+ return;
274
+ default:
275
+ throw new Error(`Unsupported FIT base type ${field.baseType}`);
276
+ }
277
+ }
278
+ getBaseTypeSize(baseType) {
279
+ switch (baseType) {
280
+ case FitBaseType.Enum:
281
+ case FitBaseType.Sint8:
282
+ case FitBaseType.Uint8:
283
+ case FitBaseType.Uint8z:
284
+ case FitBaseType.Byte:
285
+ return 1;
286
+ case FitBaseType.Sint16:
287
+ case FitBaseType.Uint16:
288
+ case FitBaseType.Uint16z:
289
+ return 2;
290
+ case FitBaseType.Sint32:
291
+ case FitBaseType.Uint32:
292
+ case FitBaseType.Uint32z:
293
+ case FitBaseType.Float32:
294
+ return 4;
295
+ case FitBaseType.Float64:
296
+ case FitBaseType.Sint64:
297
+ case FitBaseType.Uint64:
298
+ case FitBaseType.Uint64z:
299
+ return 8;
300
+ default:
301
+ return undefined;
302
+ }
303
+ }
304
+ isSupportedBaseType(baseType) {
305
+ return (baseType === FitBaseType.String
306
+ || this.getBaseTypeSize(baseType) !== undefined);
307
+ }
308
+ writeUInt8(value) {
309
+ this.data.push(value);
310
+ }
311
+ writeUInt16(value) {
312
+ this.data.push(value & UINT8_MAX, (value >>> 8) & UINT8_MAX);
313
+ }
314
+ writeInt8(value) {
315
+ this.writeUInt8(value < 0 ? 0x100 + value : value);
316
+ }
317
+ writeInt16(value) {
318
+ this.writeUInt16(value < 0 ? 0x10000 + value : value);
319
+ }
320
+ writeUInt32(value) {
321
+ this.data.push(value & UINT8_MAX, Math.floor(value / 0x100) & UINT8_MAX, Math.floor(value / 0x10000) & UINT8_MAX, Math.floor(value / 0x1000000) & UINT8_MAX);
322
+ }
323
+ writeInt32(value) {
324
+ this.writeUInt32(value < 0 ? 0x100000000 + value : value);
325
+ }
326
+ writeUInt64(value) {
327
+ for (let byteIndex = BIGINT_ZERO; byteIndex < BIGINT_EIGHT; byteIndex++) {
328
+ this.writeUInt8(Number((value >> (byteIndex * BIGINT_EIGHT)) & BIGINT_BYTE_MASK));
329
+ }
330
+ }
331
+ writeInt64(value) {
332
+ this.writeUInt64(value < BIGINT_ZERO ? UINT64_MODULUS + value : value);
333
+ }
334
+ writeFloat32(value) {
335
+ const bytes = new Uint8Array(4);
336
+ new DataView(bytes.buffer).setFloat32(0, value, true);
337
+ bytes.forEach(byte => this.writeUInt8(byte));
338
+ }
339
+ writeFloat64(value) {
340
+ const bytes = new Uint8Array(8);
341
+ new DataView(bytes.buffer).setFloat64(0, value, true);
342
+ bytes.forEach(byte => this.writeUInt8(byte));
343
+ }
344
+ static assertIntegerInRange(value, minimum, maximum, label) {
345
+ if (typeof value !== 'number'
346
+ || !Number.isInteger(value)
347
+ || value < minimum
348
+ || value > maximum) {
349
+ throw new RangeError(`${label} must be an integer between ${minimum} and ${maximum}`);
350
+ }
351
+ return value;
352
+ }
353
+ }
354
+ exports.FitEncoder = FitEncoder;
@@ -1,5 +1,7 @@
1
1
  import type { Buffer } from 'buffer';
2
2
  import type { ParsedFit } from './fit_types.js';
3
+ export { FitBaseType, FitEncoder } from './fit-encoder.js';
4
+ export type { FitEncoderField, FitEncoderOptions } from './fit-encoder.js';
3
5
  export interface FitParserOptions {
4
6
  force?: boolean;
5
7
  speedUnit?: string;
@@ -16,4 +18,3 @@ export default class FitParser {
16
18
  parseAsync(content: ArrayBuffer | Buffer<ArrayBuffer>): Promise<ParsedFit>;
17
19
  parse(content: ArrayBuffer | Buffer<ArrayBuffer>, callback: FitParserCallback): void;
18
20
  }
19
- export {};
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FitEncoder = exports.FitBaseType = void 0;
3
4
  const binary_js_1 = require("./binary.js");
4
5
  const helper_js_1 = require("./helper.js");
6
+ var fit_encoder_js_1 = require("./fit-encoder.js");
7
+ Object.defineProperty(exports, "FitBaseType", { enumerable: true, get: function () { return fit_encoder_js_1.FitBaseType; } });
8
+ Object.defineProperty(exports, "FitEncoder", { enumerable: true, get: function () { return fit_encoder_js_1.FitEncoder; } });
5
9
  class FitParser {
6
10
  constructor(options = {}) {
7
11
  this.options = {
package/dist/cjs/fit.js CHANGED
@@ -2554,6 +2554,13 @@ exports.FIT = {
2554
2554
  },
2555
2555
  20: {
2556
2556
  name: 'record',
2557
+ 90: {
2558
+ field: 'garmin_performance_condition',
2559
+ type: 'sint8',
2560
+ scale: null,
2561
+ offset: 0,
2562
+ units: '',
2563
+ },
2557
2564
  253: {
2558
2565
  field: 'timestamp',
2559
2566
  type: 'date_time',
@@ -3734,6 +3741,7 @@ exports.FIT = {
3734
3741
  offset: 0,
3735
3742
  units: '',
3736
3743
  },
3744
+ 7: { field: 'sub_sport', type: 'sub_sport', scale: null, offset: 0, units: '' },
3737
3745
  },
3738
3746
  32: {
3739
3747
  name: 'course_point',
@@ -642,6 +642,7 @@ export interface ParsedRecord {
642
642
  vertical_ratio?: number;
643
643
  stance_time_balance?: number;
644
644
  step_length?: number;
645
+ garmin_performance_condition?: number;
645
646
  absolute_pressure?: number;
646
647
  depth?: number;
647
648
  next_stop_depth?: number;
@@ -729,6 +730,7 @@ export interface ParsedCourse {
729
730
  sport?: Sport;
730
731
  name?: string;
731
732
  capabilities?: CourseCapabilities;
733
+ sub_sport?: SubSport;
732
734
  }
733
735
  export interface ParsedCoursePoint {
734
736
  timestamp: string;
@@ -0,0 +1,68 @@
1
+ /** FIT definition base-type bytes, including the endian flag where required. */
2
+ export declare enum FitBaseType {
3
+ Enum = 0,
4
+ Sint8 = 1,
5
+ Uint8 = 2,
6
+ String = 7,
7
+ Uint8z = 10,
8
+ Byte = 13,
9
+ Sint16 = 131,
10
+ Uint16 = 132,
11
+ Sint32 = 133,
12
+ Uint32 = 134,
13
+ Float32 = 136,
14
+ Float64 = 137,
15
+ Uint16z = 139,
16
+ Uint32z = 140,
17
+ Sint64 = 142,
18
+ Uint64 = 143,
19
+ Uint64z = 144
20
+ }
21
+ export interface FitEncoderField {
22
+ number: number;
23
+ size: number;
24
+ baseType: FitBaseType | number;
25
+ value: number | bigint | Uint8Array;
26
+ }
27
+ export interface FitEncoderOptions {
28
+ protocolVersion?: number;
29
+ profileVersion?: number;
30
+ }
31
+ /**
32
+ * A generic FIT binary encoder. Callers supply profile-specific field
33
+ * definitions and already-scaled field values. Numeric arrays, strings, and
34
+ * variable-length field values are supplied as raw `Uint8Array` values.
35
+ */
36
+ export declare class FitEncoder {
37
+ private readonly data;
38
+ private readonly activeDefinitions;
39
+ private readonly protocolVersion;
40
+ private readonly profileVersion;
41
+ constructor(options?: FitEncoderOptions);
42
+ writeMessage(globalMessageNumber: number, fields: FitEncoderField[], localMessageNumber?: number): this;
43
+ close(): Uint8Array;
44
+ static string(value: string): Uint8Array;
45
+ static toFitTimestamp(date: Date): number;
46
+ static calculateCRC(bytes: ArrayLike<number>): number;
47
+ private validateMessage;
48
+ private validateField;
49
+ private assertNumberInRange;
50
+ private assertBigIntInRange;
51
+ private getUnsignedMaximum;
52
+ private getFieldDefinition;
53
+ private writeDefinition;
54
+ private writeFieldValue;
55
+ private getBaseTypeSize;
56
+ private isSupportedBaseType;
57
+ private writeUInt8;
58
+ private writeUInt16;
59
+ private writeInt8;
60
+ private writeInt16;
61
+ private writeUInt32;
62
+ private writeInt32;
63
+ private writeUInt64;
64
+ private writeInt64;
65
+ private writeFloat32;
66
+ private writeFloat64;
67
+ private static assertIntegerInRange;
68
+ }
@@ -0,0 +1,350 @@
1
+ const FIT_HEADER_SIZE = 14;
2
+ const FIT_EPOCH_MS = 631065600000;
3
+ const UINT8_MAX = 0xFF;
4
+ const UINT16_MAX = 0xFFFF;
5
+ const UINT32_MAX = 0xFFFFFFFF;
6
+ const BIGINT_ZERO = BigInt(0);
7
+ const BIGINT_EIGHT = BigInt(8);
8
+ const BIGINT_BYTE_MASK = BigInt(0xFF);
9
+ const UINT64_MAX = BigInt('18446744073709551615');
10
+ const UINT64_MODULUS = BigInt('18446744073709551616');
11
+ const SINT8_MIN = -0x80;
12
+ const SINT8_MAX = 0x7F;
13
+ const SINT16_MIN = -0x8000;
14
+ const SINT16_MAX = 0x7FFF;
15
+ const SINT32_MIN = -0x80000000;
16
+ const SINT32_MAX = 0x7FFFFFFF;
17
+ const SINT64_MIN = BigInt('-9223372036854775808');
18
+ const SINT64_MAX = BigInt('9223372036854775807');
19
+ /** FIT definition base-type bytes, including the endian flag where required. */
20
+ export var FitBaseType;
21
+ (function (FitBaseType) {
22
+ FitBaseType[FitBaseType["Enum"] = 0] = "Enum";
23
+ FitBaseType[FitBaseType["Sint8"] = 1] = "Sint8";
24
+ FitBaseType[FitBaseType["Uint8"] = 2] = "Uint8";
25
+ FitBaseType[FitBaseType["String"] = 7] = "String";
26
+ FitBaseType[FitBaseType["Uint8z"] = 10] = "Uint8z";
27
+ FitBaseType[FitBaseType["Byte"] = 13] = "Byte";
28
+ FitBaseType[FitBaseType["Sint16"] = 131] = "Sint16";
29
+ FitBaseType[FitBaseType["Uint16"] = 132] = "Uint16";
30
+ FitBaseType[FitBaseType["Sint32"] = 133] = "Sint32";
31
+ FitBaseType[FitBaseType["Uint32"] = 134] = "Uint32";
32
+ FitBaseType[FitBaseType["Float32"] = 136] = "Float32";
33
+ FitBaseType[FitBaseType["Float64"] = 137] = "Float64";
34
+ FitBaseType[FitBaseType["Uint16z"] = 139] = "Uint16z";
35
+ FitBaseType[FitBaseType["Uint32z"] = 140] = "Uint32z";
36
+ FitBaseType[FitBaseType["Sint64"] = 142] = "Sint64";
37
+ FitBaseType[FitBaseType["Uint64"] = 143] = "Uint64";
38
+ FitBaseType[FitBaseType["Uint64z"] = 144] = "Uint64z";
39
+ })(FitBaseType || (FitBaseType = {}));
40
+ /**
41
+ * A generic FIT binary encoder. Callers supply profile-specific field
42
+ * definitions and already-scaled field values. Numeric arrays, strings, and
43
+ * variable-length field values are supplied as raw `Uint8Array` values.
44
+ */
45
+ export class FitEncoder {
46
+ constructor(options = {}) {
47
+ var _a, _b;
48
+ this.data = [];
49
+ this.activeDefinitions = new Map();
50
+ this.protocolVersion = FitEncoder.assertIntegerInRange((_a = options.protocolVersion) !== null && _a !== void 0 ? _a : 2, 0, UINT8_MAX, 'FIT protocol version');
51
+ this.profileVersion = FitEncoder.assertIntegerInRange((_b = options.profileVersion) !== null && _b !== void 0 ? _b : 21188, 0, UINT16_MAX, 'FIT profile version');
52
+ }
53
+ writeMessage(globalMessageNumber, fields, localMessageNumber = 0) {
54
+ this.validateMessage(globalMessageNumber, fields, localMessageNumber);
55
+ const definitionSignature = JSON.stringify({
56
+ globalMessageNumber,
57
+ fields: fields.map(field => this.getFieldDefinition(field)),
58
+ });
59
+ if (this.activeDefinitions.get(localMessageNumber) !== definitionSignature) {
60
+ this.writeDefinition(localMessageNumber, globalMessageNumber, fields);
61
+ this.activeDefinitions.set(localMessageNumber, definitionSignature);
62
+ }
63
+ this.writeUInt8(localMessageNumber);
64
+ fields.forEach(field => this.writeFieldValue(field));
65
+ return this;
66
+ }
67
+ close() {
68
+ if (this.data.length > UINT32_MAX) {
69
+ throw new RangeError('FIT data section cannot exceed 4294967295 bytes');
70
+ }
71
+ const header = [
72
+ FIT_HEADER_SIZE,
73
+ this.protocolVersion,
74
+ this.profileVersion & UINT8_MAX,
75
+ (this.profileVersion >>> 8) & UINT8_MAX,
76
+ this.data.length & UINT8_MAX,
77
+ (this.data.length >>> 8) & UINT8_MAX,
78
+ (this.data.length >>> 16) & UINT8_MAX,
79
+ (this.data.length >>> 24) & UINT8_MAX,
80
+ 0x2E,
81
+ 0x46,
82
+ 0x49,
83
+ 0x54,
84
+ ];
85
+ const headerCRC = FitEncoder.calculateCRC(header);
86
+ const output = header.concat([headerCRC & UINT8_MAX, (headerCRC >>> 8) & UINT8_MAX], this.data);
87
+ const fileCRC = FitEncoder.calculateCRC(output);
88
+ output.push(fileCRC & UINT8_MAX, (fileCRC >>> 8) & UINT8_MAX);
89
+ return new Uint8Array(output);
90
+ }
91
+ static string(value) {
92
+ const bytes = new TextEncoder().encode(value);
93
+ if (bytes.length > UINT8_MAX - 1) {
94
+ throw new RangeError('FIT string fields can contain at most 254 UTF-8 bytes plus the null terminator');
95
+ }
96
+ const output = new Uint8Array(bytes.length + 1);
97
+ output.set(bytes);
98
+ return output;
99
+ }
100
+ static toFitTimestamp(date) {
101
+ if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
102
+ throw new TypeError('FIT timestamp requires a valid Date');
103
+ }
104
+ const timestamp = Math.floor((date.getTime() - FIT_EPOCH_MS) / 1000);
105
+ return FitEncoder.assertIntegerInRange(timestamp, 0, UINT32_MAX, 'FIT timestamp');
106
+ }
107
+ static calculateCRC(bytes) {
108
+ let crc = 0;
109
+ for (let index = 0; index < bytes.length; index++) {
110
+ let value = crc ^ bytes[index];
111
+ for (let bit = 0; bit < 8; bit++) {
112
+ value = value & 1 ? (value >>> 1) ^ 0xA001 : value >>> 1;
113
+ }
114
+ crc = value;
115
+ }
116
+ return crc;
117
+ }
118
+ validateMessage(globalMessageNumber, fields, localMessageNumber) {
119
+ FitEncoder.assertIntegerInRange(globalMessageNumber, 0, UINT16_MAX, 'FIT global message number');
120
+ FitEncoder.assertIntegerInRange(localMessageNumber, 0, 0x0F, 'FIT local message number');
121
+ if (!Array.isArray(fields) || fields.length > UINT8_MAX) {
122
+ throw new RangeError('FIT message definitions support between 0 and 255 fields');
123
+ }
124
+ fields.forEach(field => this.validateField(field));
125
+ }
126
+ validateField(field) {
127
+ if (!field || typeof field !== 'object') {
128
+ throw new TypeError('FIT field definitions must be objects');
129
+ }
130
+ FitEncoder.assertIntegerInRange(field.number, 0, UINT8_MAX, 'FIT field number');
131
+ FitEncoder.assertIntegerInRange(field.size, 1, UINT8_MAX, 'FIT field size');
132
+ FitEncoder.assertIntegerInRange(field.baseType, 0, UINT8_MAX, 'FIT base type');
133
+ if (!this.isSupportedBaseType(field.baseType)) {
134
+ throw new RangeError(`Unsupported FIT base type ${field.baseType}`);
135
+ }
136
+ if (field.value instanceof Uint8Array) {
137
+ if (field.value.length !== field.size) {
138
+ throw new RangeError(`FIT field ${field.number} expected ${field.size} bytes, received ${field.value.length}`);
139
+ }
140
+ const baseTypeSize = this.getBaseTypeSize(field.baseType);
141
+ if (baseTypeSize && field.size % baseTypeSize !== 0) {
142
+ throw new RangeError(`FIT field ${field.number} size must be a multiple of ${baseTypeSize}`);
143
+ }
144
+ return;
145
+ }
146
+ const baseTypeSize = this.getBaseTypeSize(field.baseType);
147
+ if (baseTypeSize === undefined || field.size !== baseTypeSize) {
148
+ throw new RangeError(`FIT field ${field.number} has an invalid size for base type ${field.baseType}`);
149
+ }
150
+ switch (field.baseType) {
151
+ case FitBaseType.Sint64:
152
+ this.assertBigIntInRange(field.value, SINT64_MIN, SINT64_MAX, field.number);
153
+ return;
154
+ case FitBaseType.Uint64:
155
+ case FitBaseType.Uint64z:
156
+ this.assertBigIntInRange(field.value, BIGINT_ZERO, UINT64_MAX, field.number);
157
+ return;
158
+ case FitBaseType.Sint8:
159
+ this.assertNumberInRange(field.value, SINT8_MIN, SINT8_MAX, field.number);
160
+ return;
161
+ case FitBaseType.Sint16:
162
+ this.assertNumberInRange(field.value, SINT16_MIN, SINT16_MAX, field.number);
163
+ return;
164
+ case FitBaseType.Sint32:
165
+ this.assertNumberInRange(field.value, SINT32_MIN, SINT32_MAX, field.number);
166
+ return;
167
+ case FitBaseType.Float32:
168
+ if (typeof field.value !== 'number' || !Number.isFinite(field.value)) {
169
+ throw new RangeError(`FIT field ${field.number} requires a finite numeric value`);
170
+ }
171
+ if (!Number.isFinite(Math.fround(field.value))) {
172
+ throw new RangeError(`FIT field ${field.number} must be representable as a finite float32`);
173
+ }
174
+ return;
175
+ case FitBaseType.Float64:
176
+ if (typeof field.value !== 'number' || !Number.isFinite(field.value)) {
177
+ throw new RangeError(`FIT field ${field.number} requires a finite numeric value`);
178
+ }
179
+ return;
180
+ default:
181
+ this.assertNumberInRange(field.value, 0, this.getUnsignedMaximum(field.baseType), field.number);
182
+ }
183
+ }
184
+ assertNumberInRange(value, minimum, maximum, fieldNumber) {
185
+ if (typeof value !== 'number'
186
+ || !Number.isInteger(value)
187
+ || value < minimum
188
+ || value > maximum) {
189
+ throw new RangeError(`FIT field ${fieldNumber} must be an integer between ${minimum} and ${maximum}`);
190
+ }
191
+ }
192
+ assertBigIntInRange(value, minimum, maximum, fieldNumber) {
193
+ if (typeof value !== 'bigint' || value < minimum || value > maximum) {
194
+ throw new RangeError(`FIT field ${fieldNumber} must be a bigint between ${minimum} and ${maximum}`);
195
+ }
196
+ }
197
+ getUnsignedMaximum(baseType) {
198
+ switch (baseType) {
199
+ case FitBaseType.Enum:
200
+ case FitBaseType.Uint8:
201
+ case FitBaseType.Uint8z:
202
+ case FitBaseType.Byte:
203
+ return UINT8_MAX;
204
+ case FitBaseType.Uint16:
205
+ case FitBaseType.Uint16z:
206
+ return UINT16_MAX;
207
+ case FitBaseType.Uint32:
208
+ case FitBaseType.Uint32z:
209
+ return UINT32_MAX;
210
+ default:
211
+ throw new RangeError(`Unsupported FIT base type ${baseType}`);
212
+ }
213
+ }
214
+ getFieldDefinition(field) {
215
+ return { number: field.number, size: field.size, baseType: field.baseType };
216
+ }
217
+ writeDefinition(localMessageNumber, globalMessageNumber, fields) {
218
+ this.writeUInt8(0x40 | localMessageNumber);
219
+ this.writeUInt8(0);
220
+ this.writeUInt8(0);
221
+ this.writeUInt16(globalMessageNumber);
222
+ this.writeUInt8(fields.length);
223
+ fields.forEach((field) => {
224
+ this.writeUInt8(field.number);
225
+ this.writeUInt8(field.size);
226
+ this.writeUInt8(field.baseType);
227
+ });
228
+ }
229
+ writeFieldValue(field) {
230
+ if (field.value instanceof Uint8Array) {
231
+ field.value.forEach(value => this.writeUInt8(value));
232
+ return;
233
+ }
234
+ switch (field.baseType) {
235
+ case FitBaseType.Enum:
236
+ case FitBaseType.Uint8:
237
+ case FitBaseType.Uint8z:
238
+ case FitBaseType.Byte:
239
+ this.writeUInt8(field.value);
240
+ return;
241
+ case FitBaseType.Sint8:
242
+ this.writeInt8(field.value);
243
+ return;
244
+ case FitBaseType.Uint16:
245
+ case FitBaseType.Uint16z:
246
+ this.writeUInt16(field.value);
247
+ return;
248
+ case FitBaseType.Sint16:
249
+ this.writeInt16(field.value);
250
+ return;
251
+ case FitBaseType.Uint32:
252
+ case FitBaseType.Uint32z:
253
+ this.writeUInt32(field.value);
254
+ return;
255
+ case FitBaseType.Sint32:
256
+ this.writeInt32(field.value);
257
+ return;
258
+ case FitBaseType.Float32:
259
+ this.writeFloat32(field.value);
260
+ return;
261
+ case FitBaseType.Float64:
262
+ this.writeFloat64(field.value);
263
+ return;
264
+ case FitBaseType.Sint64:
265
+ this.writeInt64(field.value);
266
+ return;
267
+ case FitBaseType.Uint64:
268
+ case FitBaseType.Uint64z:
269
+ this.writeUInt64(field.value);
270
+ return;
271
+ default:
272
+ throw new Error(`Unsupported FIT base type ${field.baseType}`);
273
+ }
274
+ }
275
+ getBaseTypeSize(baseType) {
276
+ switch (baseType) {
277
+ case FitBaseType.Enum:
278
+ case FitBaseType.Sint8:
279
+ case FitBaseType.Uint8:
280
+ case FitBaseType.Uint8z:
281
+ case FitBaseType.Byte:
282
+ return 1;
283
+ case FitBaseType.Sint16:
284
+ case FitBaseType.Uint16:
285
+ case FitBaseType.Uint16z:
286
+ return 2;
287
+ case FitBaseType.Sint32:
288
+ case FitBaseType.Uint32:
289
+ case FitBaseType.Uint32z:
290
+ case FitBaseType.Float32:
291
+ return 4;
292
+ case FitBaseType.Float64:
293
+ case FitBaseType.Sint64:
294
+ case FitBaseType.Uint64:
295
+ case FitBaseType.Uint64z:
296
+ return 8;
297
+ default:
298
+ return undefined;
299
+ }
300
+ }
301
+ isSupportedBaseType(baseType) {
302
+ return (baseType === FitBaseType.String
303
+ || this.getBaseTypeSize(baseType) !== undefined);
304
+ }
305
+ writeUInt8(value) {
306
+ this.data.push(value);
307
+ }
308
+ writeUInt16(value) {
309
+ this.data.push(value & UINT8_MAX, (value >>> 8) & UINT8_MAX);
310
+ }
311
+ writeInt8(value) {
312
+ this.writeUInt8(value < 0 ? 0x100 + value : value);
313
+ }
314
+ writeInt16(value) {
315
+ this.writeUInt16(value < 0 ? 0x10000 + value : value);
316
+ }
317
+ writeUInt32(value) {
318
+ this.data.push(value & UINT8_MAX, Math.floor(value / 0x100) & UINT8_MAX, Math.floor(value / 0x10000) & UINT8_MAX, Math.floor(value / 0x1000000) & UINT8_MAX);
319
+ }
320
+ writeInt32(value) {
321
+ this.writeUInt32(value < 0 ? 0x100000000 + value : value);
322
+ }
323
+ writeUInt64(value) {
324
+ for (let byteIndex = BIGINT_ZERO; byteIndex < BIGINT_EIGHT; byteIndex++) {
325
+ this.writeUInt8(Number((value >> (byteIndex * BIGINT_EIGHT)) & BIGINT_BYTE_MASK));
326
+ }
327
+ }
328
+ writeInt64(value) {
329
+ this.writeUInt64(value < BIGINT_ZERO ? UINT64_MODULUS + value : value);
330
+ }
331
+ writeFloat32(value) {
332
+ const bytes = new Uint8Array(4);
333
+ new DataView(bytes.buffer).setFloat32(0, value, true);
334
+ bytes.forEach(byte => this.writeUInt8(byte));
335
+ }
336
+ writeFloat64(value) {
337
+ const bytes = new Uint8Array(8);
338
+ new DataView(bytes.buffer).setFloat64(0, value, true);
339
+ bytes.forEach(byte => this.writeUInt8(byte));
340
+ }
341
+ static assertIntegerInRange(value, minimum, maximum, label) {
342
+ if (typeof value !== 'number'
343
+ || !Number.isInteger(value)
344
+ || value < minimum
345
+ || value > maximum) {
346
+ throw new RangeError(`${label} must be an integer between ${minimum} and ${maximum}`);
347
+ }
348
+ return value;
349
+ }
350
+ }
@@ -1,5 +1,7 @@
1
1
  import type { Buffer } from 'buffer';
2
2
  import type { ParsedFit } from './fit_types.js';
3
+ export { FitBaseType, FitEncoder } from './fit-encoder.js';
4
+ export type { FitEncoderField, FitEncoderOptions } from './fit-encoder.js';
3
5
  export interface FitParserOptions {
4
6
  force?: boolean;
5
7
  speedUnit?: string;
@@ -16,4 +18,3 @@ export default class FitParser {
16
18
  parseAsync(content: ArrayBuffer | Buffer<ArrayBuffer>): Promise<ParsedFit>;
17
19
  parse(content: ArrayBuffer | Buffer<ArrayBuffer>, callback: FitParserCallback): void;
18
20
  }
19
- export {};
@@ -1,5 +1,6 @@
1
1
  import { calculateCRC, getArrayBuffer, readRecord } from './binary.js';
2
2
  import { mapDataIntoLap, mapDataIntoSession } from './helper.js';
3
+ export { FitBaseType, FitEncoder } from './fit-encoder.js';
3
4
  export default class FitParser {
4
5
  constructor(options = {}) {
5
6
  this.options = {
package/dist/fit.js CHANGED
@@ -2551,6 +2551,13 @@ export const FIT = {
2551
2551
  },
2552
2552
  20: {
2553
2553
  name: 'record',
2554
+ 90: {
2555
+ field: 'garmin_performance_condition',
2556
+ type: 'sint8',
2557
+ scale: null,
2558
+ offset: 0,
2559
+ units: '',
2560
+ },
2554
2561
  253: {
2555
2562
  field: 'timestamp',
2556
2563
  type: 'date_time',
@@ -3731,6 +3738,7 @@ export const FIT = {
3731
3738
  offset: 0,
3732
3739
  units: '',
3733
3740
  },
3741
+ 7: { field: 'sub_sport', type: 'sub_sport', scale: null, offset: 0, units: '' },
3734
3742
  },
3735
3743
  32: {
3736
3744
  name: 'course_point',
@@ -642,6 +642,7 @@ export interface ParsedRecord {
642
642
  vertical_ratio?: number;
643
643
  stance_time_balance?: number;
644
644
  step_length?: number;
645
+ garmin_performance_condition?: number;
645
646
  absolute_pressure?: number;
646
647
  depth?: number;
647
648
  next_stop_depth?: number;
@@ -729,6 +730,7 @@ export interface ParsedCourse {
729
730
  sport?: Sport;
730
731
  name?: string;
731
732
  capabilities?: CourseCapabilities;
733
+ sub_sport?: SubSport;
732
734
  }
733
735
  export interface ParsedCoursePoint {
734
736
  timestamp: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fit-file-parser",
3
3
  "type": "module",
4
- "version": "3.0.2",
4
+ "version": "3.1.3",
5
5
  "private": false,
6
6
  "description": "Parse your .FIT files easily, directly from JS (Garmin, Polar, Suunto)",
7
7
  "author": {
@@ -32,13 +32,6 @@
32
32
  "garmin",
33
33
  "parse"
34
34
  ],
35
- "files": [
36
- "dist/",
37
- "README.md",
38
- "LICENSE",
39
- "CHANGELOG.md",
40
- "CONTRIBUTORS.md"
41
- ],
42
35
  "exports": {
43
36
  ".": {
44
37
  "types": "./dist/fit-parser.d.ts",
@@ -47,6 +40,13 @@
47
40
  }
48
41
  },
49
42
  "main": "dist/cjs/fit-parser.js",
43
+ "files": [
44
+ "CHANGELOG.md",
45
+ "CONTRIBUTORS.md",
46
+ "LICENSE",
47
+ "README.md",
48
+ "dist/"
49
+ ],
50
50
  "maintainers": [
51
51
  {
52
52
  "email": "jimmykane9@gmail.com",
@@ -66,7 +66,8 @@
66
66
  "lint": "eslint .",
67
67
  "fmt": "eslint --fix .",
68
68
  "type-check": "tsc --noEmit",
69
- "build": "tsc --noCheck && tsc --project tsconfig.cjs.json --noCheck && echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json"
69
+ "build": "tsc --noCheck && tsc --project tsconfig.cjs.json --noCheck && echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json",
70
+ "prepack": "npm run build"
70
71
  },
71
72
  "test": "vitest",
72
73
  "dependencies": {