fit-file-parser 6.0.2 → 6.1.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.
- package/CHANGELOG.md +23 -0
- package/README.md +55 -1
- package/dist/cjs/fit-parser.d.ts +3 -0
- package/dist/cjs/fit-parser.js +17 -1
- package/dist/cjs/fit_types.d.ts +1 -1
- package/dist/cjs/profile-lookup.d.ts +19 -0
- package/dist/cjs/profile-lookup.js +150 -0
- package/dist/cjs/profile.js +2 -0
- package/dist/cjs/raw-message-reader.d.ts +60 -0
- package/dist/cjs/raw-message-reader.js +377 -0
- package/dist/fit-parser.d.ts +3 -0
- package/dist/fit-parser.js +2 -0
- package/dist/fit_types.d.ts +1 -1
- package/dist/profile-lookup.d.ts +19 -0
- package/dist/profile-lookup.js +140 -0
- package/dist/profile.js +2 -0
- package/dist/raw-message-reader.d.ts +60 -0
- package/dist/raw-message-reader.js +368 -0
- package/package.json +29 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 6.1.1 - 2026-09-21
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Export lookup helpers for FIT manufacturer, Garmin product, sport, and
|
|
8
|
+
sub-sport identifiers from the package root.
|
|
9
|
+
- Export the same helpers from the lightweight `fit-file-parser/profile`
|
|
10
|
+
entry point for lookup-only consumers.
|
|
11
|
+
- Add reverse sport, sub-sport, and course-point identifier lookups.
|
|
12
|
+
- Add lightweight `fit-file-parser/raw` and `fit-file-parser/encoder` entry
|
|
13
|
+
points for profile-independent message scanning and encoding.
|
|
14
|
+
- Add a strict raw-message reader with CRC, definition, developer-field, and
|
|
15
|
+
compressed-timestamp validation.
|
|
16
|
+
- Export an opt-in Garmin product display-name helper without changing parsed
|
|
17
|
+
`product` or `product_name` fields.
|
|
18
|
+
- Decode mountain enduro and mountain downhill sub-sport identifiers.
|
|
19
|
+
|
|
20
|
+
### Compatibility
|
|
21
|
+
|
|
22
|
+
- Existing parsed output remains unchanged except that sub-sport IDs 153 and
|
|
23
|
+
154 now resolve to `mountain_enduro` and `mountain_downhill`.
|
|
24
|
+
- Existing Garmin product display names ported from SportsLib are preserved.
|
|
25
|
+
|
|
3
26
|
## 6.0.2 - 2026-09-21
|
|
4
27
|
|
|
5
28
|
### Changed
|
package/README.md
CHANGED
|
@@ -257,6 +257,57 @@ unknown enum IDs remain numbers, and invalid entries retained inside FIT arrays
|
|
|
257
257
|
are `null`. All profile fields are optional because each FIT message definition
|
|
258
258
|
chooses which fields are present.
|
|
259
259
|
|
|
260
|
+
Applications that need profile lookups independently of parsing can use the
|
|
261
|
+
exported manufacturer, Garmin product, sport, sub-sport, and course-point helpers:
|
|
262
|
+
|
|
263
|
+
```javascript
|
|
264
|
+
import {
|
|
265
|
+
getFitCoursePointId,
|
|
266
|
+
getFitGarminProductDisplayName,
|
|
267
|
+
getFitManufacturerName,
|
|
268
|
+
getFitSportId,
|
|
269
|
+
getFitSportName,
|
|
270
|
+
getFitSubSportId,
|
|
271
|
+
getFitSubSportName,
|
|
272
|
+
} from 'fit-file-parser'
|
|
273
|
+
|
|
274
|
+
getFitManufacturerName(1) // "garmin"
|
|
275
|
+
getFitGarminProductDisplayName(4655) // "Edge MTB"
|
|
276
|
+
getFitSportName(2) // "cycling"
|
|
277
|
+
getFitSubSportName(153) // "mountain_enduro"
|
|
278
|
+
getFitSportId('cycling') // 2
|
|
279
|
+
getFitSubSportId('indoor_cycling') // 6
|
|
280
|
+
getFitCoursePointId('rest_area') // 29
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
These helpers read the same maintained profile used by the decoder. The product
|
|
284
|
+
display helper is opt-in and does not synthesize or overwrite parsed
|
|
285
|
+
`product_name` values.
|
|
286
|
+
|
|
287
|
+
Lookup-only consumers can import the same helpers from
|
|
288
|
+
`fit-file-parser/profile` without loading the parser entry point.
|
|
289
|
+
|
|
290
|
+
### Lightweight raw messages
|
|
291
|
+
|
|
292
|
+
Consumers that need strict FIT framing, CRC validation, compressed timestamps,
|
|
293
|
+
and exact fields without loading the semantic profile can use the lightweight
|
|
294
|
+
raw entry point:
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
import { readFitMessages } from 'fit-file-parser/raw'
|
|
298
|
+
|
|
299
|
+
const { messages } = readFitMessages(content, {
|
|
300
|
+
messageNumbers: [18, 26, 72],
|
|
301
|
+
maxInputBytes: 64 * 1024 * 1024,
|
|
302
|
+
})
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The result retains native and developer fields as defensive `Uint8Array`
|
|
306
|
+
copies. It does not apply names, enum formatting, scales, units, or
|
|
307
|
+
provider-specific interpretation. Invalid input throws `FitMessageReaderError`
|
|
308
|
+
with a stable `code` such as `invalid_header`, `invalid_crc`, or
|
|
309
|
+
`invalid_structure`.
|
|
310
|
+
|
|
260
311
|
## Inputs
|
|
261
312
|
|
|
262
313
|
Both parser methods accept:
|
|
@@ -283,7 +334,7 @@ base types, sizes, and values in their raw FIT representation. Applying FIT
|
|
|
283
334
|
scales and offsets is the caller's responsibility.
|
|
284
335
|
|
|
285
336
|
```javascript
|
|
286
|
-
import { FitBaseType, FitEncoder } from 'fit-file-parser'
|
|
337
|
+
import { FitBaseType, FitEncoder } from 'fit-file-parser/encoder'
|
|
287
338
|
|
|
288
339
|
const encoder = new FitEncoder()
|
|
289
340
|
encoder.writeMessage(0, [
|
|
@@ -319,6 +370,9 @@ Scalar 64-bit values use `bigint`. Strings, numeric arrays, and other
|
|
|
319
370
|
variable-length values use exact-size `Uint8Array` values. Invalid field
|
|
320
371
|
definitions or numeric ranges throw before a partial message is written.
|
|
321
372
|
|
|
373
|
+
The encoder remains available from the package root; the `/encoder` entry
|
|
374
|
+
point avoids loading the decoder and semantic profile.
|
|
375
|
+
|
|
322
376
|
## TypeScript and module formats
|
|
323
377
|
|
|
324
378
|
The package includes TypeScript declarations and exports
|
package/dist/cjs/fit-parser.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ import type { ParsedFit } from './fit_types.js';
|
|
|
3
3
|
export { FitBaseType, FitEncoder } from './fit-encoder.js';
|
|
4
4
|
export type { FitEncoderField, FitEncoderOptions } from './fit-encoder.js';
|
|
5
5
|
export type { ParsedFit, ParsedRawDeveloperField, ParsedRawFitField, ParsedRawFitMessage, ParsedRawFitMessageDeveloperField, } from './fit_types.js';
|
|
6
|
+
export { getFitCoursePointId, getFitGarminProductDisplayName, getFitGarminProductName, getFitManufacturerName, getFitSportId, getFitSportName, getFitSubSportId, getFitSubSportName, } from './profile-lookup.js';
|
|
7
|
+
export { FitMessageReaderError, fitTimestampToUnixMilliseconds, getFitBaseTypeId, readFitMessages, readFitStringField, readFitUnsignedField, } from './raw-message-reader.js';
|
|
8
|
+
export type { FitMessageReaderErrorCode, FitMessageReaderIssue, FitMessageReaderIssueCode, FitMessageReaderOptions, FitMessageReaderResult, FitRawDeveloperField, FitRawField, FitRawMessage, } from './raw-message-reader.js';
|
|
6
9
|
export interface FitParserOptions {
|
|
7
10
|
force?: boolean;
|
|
8
11
|
speedUnit?: string;
|
package/dist/cjs/fit-parser.js
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FitEncoder = exports.FitBaseType = void 0;
|
|
3
|
+
exports.readFitUnsignedField = exports.readFitStringField = exports.readFitMessages = exports.getFitBaseTypeId = exports.fitTimestampToUnixMilliseconds = exports.FitMessageReaderError = exports.getFitSubSportName = exports.getFitSubSportId = exports.getFitSportName = exports.getFitSportId = exports.getFitManufacturerName = exports.getFitGarminProductName = exports.getFitGarminProductDisplayName = exports.getFitCoursePointId = exports.FitEncoder = exports.FitBaseType = void 0;
|
|
4
4
|
const binary_js_1 = require("./binary.js");
|
|
5
5
|
const helper_js_1 = require("./helper.js");
|
|
6
6
|
var fit_encoder_js_1 = require("./fit-encoder.js");
|
|
7
7
|
Object.defineProperty(exports, "FitBaseType", { enumerable: true, get: function () { return fit_encoder_js_1.FitBaseType; } });
|
|
8
8
|
Object.defineProperty(exports, "FitEncoder", { enumerable: true, get: function () { return fit_encoder_js_1.FitEncoder; } });
|
|
9
|
+
var profile_lookup_js_1 = require("./profile-lookup.js");
|
|
10
|
+
Object.defineProperty(exports, "getFitCoursePointId", { enumerable: true, get: function () { return profile_lookup_js_1.getFitCoursePointId; } });
|
|
11
|
+
Object.defineProperty(exports, "getFitGarminProductDisplayName", { enumerable: true, get: function () { return profile_lookup_js_1.getFitGarminProductDisplayName; } });
|
|
12
|
+
Object.defineProperty(exports, "getFitGarminProductName", { enumerable: true, get: function () { return profile_lookup_js_1.getFitGarminProductName; } });
|
|
13
|
+
Object.defineProperty(exports, "getFitManufacturerName", { enumerable: true, get: function () { return profile_lookup_js_1.getFitManufacturerName; } });
|
|
14
|
+
Object.defineProperty(exports, "getFitSportId", { enumerable: true, get: function () { return profile_lookup_js_1.getFitSportId; } });
|
|
15
|
+
Object.defineProperty(exports, "getFitSportName", { enumerable: true, get: function () { return profile_lookup_js_1.getFitSportName; } });
|
|
16
|
+
Object.defineProperty(exports, "getFitSubSportId", { enumerable: true, get: function () { return profile_lookup_js_1.getFitSubSportId; } });
|
|
17
|
+
Object.defineProperty(exports, "getFitSubSportName", { enumerable: true, get: function () { return profile_lookup_js_1.getFitSubSportName; } });
|
|
18
|
+
var raw_message_reader_js_1 = require("./raw-message-reader.js");
|
|
19
|
+
Object.defineProperty(exports, "FitMessageReaderError", { enumerable: true, get: function () { return raw_message_reader_js_1.FitMessageReaderError; } });
|
|
20
|
+
Object.defineProperty(exports, "fitTimestampToUnixMilliseconds", { enumerable: true, get: function () { return raw_message_reader_js_1.fitTimestampToUnixMilliseconds; } });
|
|
21
|
+
Object.defineProperty(exports, "getFitBaseTypeId", { enumerable: true, get: function () { return raw_message_reader_js_1.getFitBaseTypeId; } });
|
|
22
|
+
Object.defineProperty(exports, "readFitMessages", { enumerable: true, get: function () { return raw_message_reader_js_1.readFitMessages; } });
|
|
23
|
+
Object.defineProperty(exports, "readFitStringField", { enumerable: true, get: function () { return raw_message_reader_js_1.readFitStringField; } });
|
|
24
|
+
Object.defineProperty(exports, "readFitUnsignedField", { enumerable: true, get: function () { return raw_message_reader_js_1.readFitUnsignedField; } });
|
|
9
25
|
class FitParser {
|
|
10
26
|
constructor(options = {}) {
|
|
11
27
|
var _a, _b, _c, _d;
|
package/dist/cjs/fit_types.d.ts
CHANGED
|
@@ -213,7 +213,7 @@ export type SportEvent = 'uncategorized' | 'geocaching' | 'fitness' | 'recreatio
|
|
|
213
213
|
export type SquatExerciseName = 'leg_press' | 'back_squat_with_body_bar' | 'back_squats' | 'weighted_back_squats' | 'balancing_squat' | 'weighted_balancing_squat' | 'barbell_back_squat' | 'barbell_box_squat' | 'barbell_front_squat' | 'barbell_hack_squat' | 'barbell_hang_squat_snatch' | 'barbell_lateral_step_up' | 'barbell_quarter_squat' | 'barbell_siff_squat' | 'barbell_squat_snatch' | 'barbell_squat_with_heels_raised' | 'barbell_stepover' | 'barbell_step_up' | 'bench_squat_with_rotational_chop' | 'weighted_bench_squat_with_rotational_chop' | 'body_weight_wall_squat' | 'weighted_wall_squat' | 'box_step_squat' | 'weighted_box_step_squat' | 'braced_squat' | 'crossed_arm_barbell_front_squat' | 'crossover_dumbbell_step_up' | 'dumbbell_front_squat' | 'dumbbell_split_squat' | 'dumbbell_squat' | 'dumbbell_squat_clean' | 'dumbbell_stepover' | 'dumbbell_step_up' | 'elevated_single_leg_squat' | 'weighted_elevated_single_leg_squat' | 'figure_four_squats' | 'weighted_figure_four_squats' | 'goblet_squat' | 'kettlebell_squat' | 'kettlebell_swing_overhead' | 'kettlebell_swing_with_flip_to_squat' | 'lateral_dumbbell_step_up' | 'one_legged_squat' | 'overhead_dumbbell_squat' | 'overhead_squat' | 'partial_single_leg_squat' | 'weighted_partial_single_leg_squat' | 'pistol_squat' | 'weighted_pistol_squat' | 'plie_slides' | 'weighted_plie_slides' | 'plie_squat' | 'weighted_plie_squat' | 'prisoner_squat' | 'weighted_prisoner_squat' | 'single_leg_bench_get_up' | 'weighted_single_leg_bench_get_up' | 'single_leg_bench_squat' | 'weighted_single_leg_bench_squat' | 'single_leg_squat_on_swiss_ball' | 'weighted_single_leg_squat_on_swiss_ball' | 'squat' | 'weighted_squat' | 'squats_with_band' | 'staggered_squat' | 'weighted_staggered_squat' | 'step_up' | 'weighted_step_up' | 'suitcase_squats' | 'sumo_squat' | 'sumo_squat_slide_in' | 'weighted_sumo_squat_slide_in' | 'sumo_squat_to_high_pull' | 'sumo_squat_to_stand' | 'weighted_sumo_squat_to_stand' | 'sumo_squat_with_rotation' | 'weighted_sumo_squat_with_rotation' | 'swiss_ball_body_weight_wall_squat' | 'weighted_swiss_ball_wall_squat' | 'thrusters' | 'uneven_squat' | 'weighted_uneven_squat' | 'waist_slimming_squat' | 'wall_ball' | 'wide_stance_barbell_squat' | 'wide_stance_goblet_squat' | 'zercher_squat' | 'kbs_overhead' | 'squat_and_side_kick' | 'squat_jumps_in_n_out' | 'pilates_plie_squats_parallel_turned_out_flat_and_heels' | 'releve_straight_leg_and_knee_bent_with_one_leg_variation' | 'alternating_box_dumbbell_step_ups' | 'dumbbell_overhead_squat_single_arm' | 'dumbbell_squat_snatch' | 'medicine_ball_squat' | 'wall_ball_squat_and_press' | 'squat_american_swing' | 'air_squat' | 'dumbbell_thrusters' | 'overhead_barbell_squat' | number;
|
|
214
214
|
export type StairStepperExerciseName = 'stair_stepper' | number;
|
|
215
215
|
export type StrokeType = 'no_event' | 'other' | 'serve' | 'forehand' | 'backhand' | 'smash' | number;
|
|
216
|
-
export type SubSport = 'generic' | 'treadmill' | 'street' | 'trail' | 'track' | 'spin' | 'indoor_cycling' | 'road' | 'mountain' | 'downhill' | 'recumbent' | 'cyclocross' | 'hand_cycling' | 'track_cycling' | 'indoor_rowing' | 'elliptical' | 'stair_climbing' | 'lap_swimming' | 'open_water' | 'flexibility_training' | 'strength_training' | 'warm_up' | 'match' | 'exercise' | 'challenge' | 'indoor_skiing' | 'cardio_training' | 'indoor_walking' | 'e_bike_fitness' | 'bmx' | 'casual_walking' | 'speed_walking' | 'bike_to_run_transition' | 'run_to_bike_transition' | 'swim_to_bike_transition' | 'atv' | 'motocross' | 'backcountry' | 'resort' | 'rc_drone' | 'wingsuit' | 'whitewater' | 'skate_skiing' | 'yoga' | 'pilates' | 'indoor_running' | 'gravel_cycling' | 'e_bike_mountain' | 'commuting' | 'mixed_surface' | 'navigate' | 'track_me' | 'map' | 'single_gas_diving' | 'multi_gas_diving' | 'gauge_diving' | 'apnea_diving' | 'apnea_hunting' | 'virtual_activity' | 'obstacle' | 'breathing' | 'ccr_diving' | 'sail_race' | 'expedition' | 'ultra' | 'indoor_climbing' | 'bouldering' | 'hiit' | 'indoor_grinding' | 'hunting_with_dogs' | 'amrap' | 'emom' | 'tabata' | 'esport' | 'triathlon' | 'duathlon' | 'brick' | 'swim_run' | 'adventure_race' | 'trucker_workout' | 'pickleball' | 'padel' | 'indoor_wheelchair_walk' | 'indoor_wheelchair_run' | 'indoor_hand_cycling' | 'field' | 'ice' | 'ultimate' | 'platform' | 'squash' | 'badminton' | 'racquetball' | 'table_tennis' | 'overland' | 'trolling_motor' | 'fly_canopy' | 'fly_paraglide' | 'fly_paramotor' | 'fly_pressurized' | 'fly_navigate' | 'fly_timer' | 'fly_altimeter' | 'fly_wx' | 'fly_vfr' | 'fly_ifr' | 'dynamic_apnea' | 'enduro' | 'rucking' | 'rally' | 'pool_triathlon' | 'e_bike_enduro' | 'all' | number;
|
|
216
|
+
export type SubSport = 'generic' | 'treadmill' | 'street' | 'trail' | 'track' | 'spin' | 'indoor_cycling' | 'road' | 'mountain' | 'downhill' | 'recumbent' | 'cyclocross' | 'hand_cycling' | 'track_cycling' | 'indoor_rowing' | 'elliptical' | 'stair_climbing' | 'lap_swimming' | 'open_water' | 'flexibility_training' | 'strength_training' | 'warm_up' | 'match' | 'exercise' | 'challenge' | 'indoor_skiing' | 'cardio_training' | 'indoor_walking' | 'e_bike_fitness' | 'bmx' | 'casual_walking' | 'speed_walking' | 'bike_to_run_transition' | 'run_to_bike_transition' | 'swim_to_bike_transition' | 'atv' | 'motocross' | 'backcountry' | 'resort' | 'rc_drone' | 'wingsuit' | 'whitewater' | 'skate_skiing' | 'yoga' | 'pilates' | 'indoor_running' | 'gravel_cycling' | 'e_bike_mountain' | 'commuting' | 'mixed_surface' | 'navigate' | 'track_me' | 'map' | 'single_gas_diving' | 'multi_gas_diving' | 'gauge_diving' | 'apnea_diving' | 'apnea_hunting' | 'virtual_activity' | 'obstacle' | 'breathing' | 'ccr_diving' | 'sail_race' | 'expedition' | 'ultra' | 'indoor_climbing' | 'bouldering' | 'hiit' | 'indoor_grinding' | 'hunting_with_dogs' | 'amrap' | 'emom' | 'tabata' | 'esport' | 'triathlon' | 'duathlon' | 'brick' | 'swim_run' | 'adventure_race' | 'trucker_workout' | 'pickleball' | 'padel' | 'indoor_wheelchair_walk' | 'indoor_wheelchair_run' | 'indoor_hand_cycling' | 'field' | 'ice' | 'ultimate' | 'platform' | 'squash' | 'badminton' | 'racquetball' | 'table_tennis' | 'overland' | 'trolling_motor' | 'fly_canopy' | 'fly_paraglide' | 'fly_paramotor' | 'fly_pressurized' | 'fly_navigate' | 'fly_timer' | 'fly_altimeter' | 'fly_wx' | 'fly_vfr' | 'fly_ifr' | 'dynamic_apnea' | 'enduro' | 'rucking' | 'rally' | 'pool_triathlon' | 'e_bike_enduro' | 'mountain_enduro' | 'mountain_downhill' | 'all' | number;
|
|
217
217
|
export type SupportedExdScreenLayouts = 'full_screen' | 'half_vertical' | 'half_horizontal' | 'half_vertical_right_split' | 'half_horizontal_bottom_split' | 'full_quarter_split' | 'half_vertical_left_split' | 'half_horizontal_top_split' | number;
|
|
218
218
|
export type SuspensionExerciseName = 'chest_fly' | 'chest_press' | 'crunch' | 'curl' | 'dip' | 'face_pull' | 'glute_bridge' | 'hamstring_curl' | 'hip_drop' | 'inverted_row' | 'knee_drive_jump' | 'knee_to_chest' | 'lat_pullover' | 'lunge' | 'mountain_climber' | 'pendulum' | 'pike' | 'plank' | 'power_pull' | 'pull_up' | 'push_up' | 'reverse_mountain_climber' | 'reverse_plank' | 'rollout' | 'row' | 'side_lunge' | 'side_plank' | 'single_leg_deadlift' | 'single_leg_squat' | 'sit_up' | 'split' | 'squat' | 'squat_jump' | 'tricep_press' | 'y_fly' | number;
|
|
219
219
|
export type SwimStroke = 'freestyle' | 'backstroke' | 'breaststroke' | 'butterfly' | 'drill' | 'mixed' | 'im' | 'im_by_round' | 'rimo' | number;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Resolves a FIT manufacturer identifier to its canonical profile name. */
|
|
2
|
+
export declare function getFitManufacturerName(value: number | string | null | undefined): string | null;
|
|
3
|
+
/** Resolves a Garmin product identifier to its canonical profile name. */
|
|
4
|
+
export declare function getFitGarminProductName(value: number | string | null | undefined): string | null;
|
|
5
|
+
/** Resolves a FIT sport identifier to its canonical profile name. */
|
|
6
|
+
export declare function getFitSportName(value: number | string | null | undefined): string | null;
|
|
7
|
+
/** Resolves a FIT sub-sport identifier to its canonical profile name. */
|
|
8
|
+
export declare function getFitSubSportName(value: number | string | null | undefined): string | null;
|
|
9
|
+
/** Resolves a FIT sport name to its numeric profile identifier. */
|
|
10
|
+
export declare function getFitSportId(value: string | null | undefined): number | null;
|
|
11
|
+
/** Resolves a FIT sub-sport name to its numeric profile identifier. */
|
|
12
|
+
export declare function getFitSubSportId(value: string | null | undefined): number | null;
|
|
13
|
+
/** Resolves a FIT course-point name to its numeric profile identifier. */
|
|
14
|
+
export declare function getFitCoursePointId(value: string | null | undefined): number | null;
|
|
15
|
+
/**
|
|
16
|
+
* Resolves a Garmin product identifier to a human-readable device name.
|
|
17
|
+
* This does not alter parsed `product` or `product_name` fields.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getFitGarminProductDisplayName(value: number | string | null | undefined): string | null;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getFitManufacturerName = getFitManufacturerName;
|
|
4
|
+
exports.getFitGarminProductName = getFitGarminProductName;
|
|
5
|
+
exports.getFitSportName = getFitSportName;
|
|
6
|
+
exports.getFitSubSportName = getFitSubSportName;
|
|
7
|
+
exports.getFitSportId = getFitSportId;
|
|
8
|
+
exports.getFitSubSportId = getFitSubSportId;
|
|
9
|
+
exports.getFitCoursePointId = getFitCoursePointId;
|
|
10
|
+
exports.getFitGarminProductDisplayName = getFitGarminProductDisplayName;
|
|
11
|
+
const profile_js_1 = require("./profile.js");
|
|
12
|
+
function normalizeProfileName(value) {
|
|
13
|
+
return value.trim().toLowerCase().replace(/[\s_-]/g, '');
|
|
14
|
+
}
|
|
15
|
+
const FIT_PROFILE_MANUFACTURERS = profile_js_1.PROFILE_TYPES.manufacturer;
|
|
16
|
+
const FIT_PROFILE_GARMIN_PRODUCTS = profile_js_1.PROFILE_TYPES.garmin_product;
|
|
17
|
+
const FIT_PROFILE_SPORTS = profile_js_1.PROFILE_TYPES.sport;
|
|
18
|
+
const FIT_PROFILE_SUB_SPORTS = profile_js_1.PROFILE_TYPES.sub_sport;
|
|
19
|
+
const FIT_PROFILE_COURSE_POINTS = profile_js_1.PROFILE_TYPES.course_point;
|
|
20
|
+
function createProfileIdMap(mapping) {
|
|
21
|
+
return new Map(Object.entries(mapping)
|
|
22
|
+
.filter((entry) => typeof entry[1] === 'string')
|
|
23
|
+
.map(([id, name]) => [normalizeProfileName(name), Number(id)]));
|
|
24
|
+
}
|
|
25
|
+
const FIT_PROFILE_SPORT_IDS = createProfileIdMap(FIT_PROFILE_SPORTS);
|
|
26
|
+
const FIT_PROFILE_SUB_SPORT_IDS = createProfileIdMap(FIT_PROFILE_SUB_SPORTS);
|
|
27
|
+
const FIT_PROFILE_COURSE_POINT_IDS = createProfileIdMap(FIT_PROFILE_COURSE_POINTS);
|
|
28
|
+
function getProfileName(mapping, value) {
|
|
29
|
+
if (value === null || value === undefined) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
if (typeof value !== 'number' && typeof value !== 'string') {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const normalizedValue = typeof value === 'string' ? value.trim() : value;
|
|
36
|
+
if (normalizedValue === '' || (typeof normalizedValue === 'string' && !/^\d+$/.test(normalizedValue))) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const id = Number(normalizedValue);
|
|
40
|
+
if (!Number.isSafeInteger(id) || id < 0) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const name = mapping[id];
|
|
44
|
+
return typeof name === 'string' ? name : null;
|
|
45
|
+
}
|
|
46
|
+
function getProfileId(mapping, value) {
|
|
47
|
+
var _a;
|
|
48
|
+
if (typeof value !== 'string') {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const name = normalizeProfileName(value);
|
|
52
|
+
if (name === '') {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return (_a = mapping.get(name)) !== null && _a !== void 0 ? _a : null;
|
|
56
|
+
}
|
|
57
|
+
/** Resolves a FIT manufacturer identifier to its canonical profile name. */
|
|
58
|
+
function getFitManufacturerName(value) {
|
|
59
|
+
return getProfileName(FIT_PROFILE_MANUFACTURERS, value);
|
|
60
|
+
}
|
|
61
|
+
/** Resolves a Garmin product identifier to its canonical profile name. */
|
|
62
|
+
function getFitGarminProductName(value) {
|
|
63
|
+
return getProfileName(FIT_PROFILE_GARMIN_PRODUCTS, value);
|
|
64
|
+
}
|
|
65
|
+
/** Resolves a FIT sport identifier to its canonical profile name. */
|
|
66
|
+
function getFitSportName(value) {
|
|
67
|
+
return getProfileName(FIT_PROFILE_SPORTS, value);
|
|
68
|
+
}
|
|
69
|
+
/** Resolves a FIT sub-sport identifier to its canonical profile name. */
|
|
70
|
+
function getFitSubSportName(value) {
|
|
71
|
+
return getProfileName(FIT_PROFILE_SUB_SPORTS, value);
|
|
72
|
+
}
|
|
73
|
+
/** Resolves a FIT sport name to its numeric profile identifier. */
|
|
74
|
+
function getFitSportId(value) {
|
|
75
|
+
return getProfileId(FIT_PROFILE_SPORT_IDS, value);
|
|
76
|
+
}
|
|
77
|
+
/** Resolves a FIT sub-sport name to its numeric profile identifier. */
|
|
78
|
+
function getFitSubSportId(value) {
|
|
79
|
+
return getProfileId(FIT_PROFILE_SUB_SPORT_IDS, value);
|
|
80
|
+
}
|
|
81
|
+
/** Resolves a FIT course-point name to its numeric profile identifier. */
|
|
82
|
+
function getFitCoursePointId(value) {
|
|
83
|
+
return getProfileId(FIT_PROFILE_COURSE_POINT_IDS, value);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Resolves a Garmin product identifier to a human-readable device name.
|
|
87
|
+
* This does not alter parsed `product` or `product_name` fields.
|
|
88
|
+
*/
|
|
89
|
+
function getFitGarminProductDisplayName(value) {
|
|
90
|
+
const name = getFitGarminProductName(value);
|
|
91
|
+
if (!name) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
// Preserve the established display spelling for the optical heart-rate product.
|
|
95
|
+
const displaySource = name === 'o_hr' ? 'o_h_r' : name;
|
|
96
|
+
const formatted = displaySource
|
|
97
|
+
.replace(/^fr(\d+)/i, 'Forerunner $1')
|
|
98
|
+
.replace(/^fenix(\d+)/i, 'Fenix $1')
|
|
99
|
+
.replace(/^edge(\d+)/i, 'Edge $1')
|
|
100
|
+
.replace(/^vivoactive/i, 'VivoActive')
|
|
101
|
+
.replace(/^vivosmart/i, 'VivoSmart')
|
|
102
|
+
.replace(/^vivofit/i, 'VivoFit')
|
|
103
|
+
.replace(/^vivomove/i, 'VivoMove')
|
|
104
|
+
.replace(/^vivosport/i, 'VivoSport')
|
|
105
|
+
.replace(/^approach([A-Z\d])/i, 'Approach $1')
|
|
106
|
+
.replace(/^marq([A-Z])/i, 'Marq $1')
|
|
107
|
+
.replace(/^hrm/i, 'HRM ')
|
|
108
|
+
.replace(/_/g, ' ')
|
|
109
|
+
.replace(/([a-z])([A-Z0-9])/g, '$1 $2')
|
|
110
|
+
.replace(/(\d)([a-z])/gi, '$1 $2');
|
|
111
|
+
return formatted
|
|
112
|
+
.split(' ')
|
|
113
|
+
.map((word) => {
|
|
114
|
+
const lower = word.toLowerCase();
|
|
115
|
+
if (lower === 'apac')
|
|
116
|
+
return 'APAC';
|
|
117
|
+
if (lower === 'xt')
|
|
118
|
+
return 'XT';
|
|
119
|
+
if (lower === 'lte')
|
|
120
|
+
return 'LTE';
|
|
121
|
+
if (lower === 'hr')
|
|
122
|
+
return 'HR';
|
|
123
|
+
if (lower === 'gps')
|
|
124
|
+
return 'GPS';
|
|
125
|
+
if (lower === 'mtb')
|
|
126
|
+
return 'MTB';
|
|
127
|
+
if (lower === 'ii')
|
|
128
|
+
return 'II';
|
|
129
|
+
if (lower === 'iii')
|
|
130
|
+
return 'III';
|
|
131
|
+
if (lower === 'm' && name.toLowerCase().includes('645m'))
|
|
132
|
+
return 'Music';
|
|
133
|
+
if (lower === 'jpn')
|
|
134
|
+
return 'Japan';
|
|
135
|
+
if (lower === 'chn')
|
|
136
|
+
return 'China';
|
|
137
|
+
if (lower === 'twn')
|
|
138
|
+
return 'Taiwan';
|
|
139
|
+
if (lower === 'kor')
|
|
140
|
+
return 'Korea';
|
|
141
|
+
if (lower === 'rus')
|
|
142
|
+
return 'Russia';
|
|
143
|
+
if (lower === 'sea')
|
|
144
|
+
return 'SEA';
|
|
145
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
146
|
+
})
|
|
147
|
+
.join(' ')
|
|
148
|
+
.replace(/Vivo Active/g, 'VivoActive')
|
|
149
|
+
.trim();
|
|
150
|
+
}
|
package/dist/cjs/profile.js
CHANGED
|
@@ -17479,6 +17479,8 @@ exports.PROFILE_TYPES = {
|
|
|
17479
17479
|
125: 'rally',
|
|
17480
17480
|
126: 'pool_triathlon',
|
|
17481
17481
|
127: 'e_bike_enduro',
|
|
17482
|
+
153: 'mountain_enduro',
|
|
17483
|
+
154: 'mountain_downhill',
|
|
17482
17484
|
254: 'all',
|
|
17483
17485
|
},
|
|
17484
17486
|
supported_exd_screen_layouts: {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export type FitMessageReaderErrorCode = 'invalid_input' | 'input_limit' | 'invalid_header' | 'invalid_crc' | 'invalid_structure';
|
|
2
|
+
export type FitMessageReaderIssueCode = 'invalid_timestamp';
|
|
3
|
+
export interface FitRawField {
|
|
4
|
+
fieldNumber: number;
|
|
5
|
+
size: number;
|
|
6
|
+
baseType: number;
|
|
7
|
+
bytes: Uint8Array;
|
|
8
|
+
}
|
|
9
|
+
export interface FitRawDeveloperField {
|
|
10
|
+
fieldNumber: number;
|
|
11
|
+
size: number;
|
|
12
|
+
developerDataIndex: number;
|
|
13
|
+
bytes: Uint8Array;
|
|
14
|
+
}
|
|
15
|
+
export interface FitRawMessage {
|
|
16
|
+
globalMessageNumber: number;
|
|
17
|
+
messageIndex: number;
|
|
18
|
+
littleEndian: boolean;
|
|
19
|
+
/** Native FIT timestamp in seconds since the FIT epoch, when available. */
|
|
20
|
+
timestamp?: number;
|
|
21
|
+
/** Reconstructed native timestamp when the record used a compressed header. */
|
|
22
|
+
compressedTimestamp?: number;
|
|
23
|
+
fields: FitRawField[];
|
|
24
|
+
developerFields: FitRawDeveloperField[];
|
|
25
|
+
}
|
|
26
|
+
export interface FitMessageReaderIssue {
|
|
27
|
+
code: FitMessageReaderIssueCode;
|
|
28
|
+
globalMessageNumber: number;
|
|
29
|
+
messageIndex: number;
|
|
30
|
+
}
|
|
31
|
+
export interface FitMessageReaderResult {
|
|
32
|
+
protocolVersion: number;
|
|
33
|
+
profileVersion: number;
|
|
34
|
+
messages: FitRawMessage[];
|
|
35
|
+
issues: FitMessageReaderIssue[];
|
|
36
|
+
}
|
|
37
|
+
export interface FitMessageReaderOptions {
|
|
38
|
+
/** Retain only these global message numbers. Omit to retain every message. */
|
|
39
|
+
messageNumbers?: readonly number[];
|
|
40
|
+
/** Reject larger inputs before allocating retained message data. */
|
|
41
|
+
maxInputBytes?: number;
|
|
42
|
+
}
|
|
43
|
+
/** Error thrown when the strict raw-message reader rejects a FIT input. */
|
|
44
|
+
export declare class FitMessageReaderError extends Error {
|
|
45
|
+
readonly code: FitMessageReaderErrorCode;
|
|
46
|
+
constructor(code: FitMessageReaderErrorCode);
|
|
47
|
+
}
|
|
48
|
+
/** Returns the five-bit FIT base-type identifier, or null for reserved/unknown values. */
|
|
49
|
+
export declare function getFitBaseTypeId(baseType: number): number | null;
|
|
50
|
+
/** Reads an unsigned one-, two-, or four-byte raw FIT field. */
|
|
51
|
+
export declare function readFitUnsignedField(field: FitRawField | undefined, expectedBaseType: number, size: 1 | 2 | 4, littleEndian: boolean): number | undefined;
|
|
52
|
+
/** Reads UTF-8 bytes, removes trailing NUL padding, and preserves interior NUL separators. */
|
|
53
|
+
export declare function readFitStringField(field: FitRawField | undefined): string | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Strictly validates a FIT file and retains raw fields for selected messages.
|
|
56
|
+
* This entry point does not load the semantic FIT profile or format field values.
|
|
57
|
+
*/
|
|
58
|
+
export declare function readFitMessages(input: ArrayBuffer | Uint8Array, options?: FitMessageReaderOptions): FitMessageReaderResult;
|
|
59
|
+
/** Converts a native FIT timestamp to a JavaScript epoch millisecond value. */
|
|
60
|
+
export declare function fitTimestampToUnixMilliseconds(timestamp: number): number;
|