@kaiord/fit 4.8.1 → 7.0.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.
- package/dist/index.js +278 -262
- package/dist/index.js.map +1 -1
- package/package.json +10 -7
package/dist/index.js
CHANGED
|
@@ -3,6 +3,13 @@ import { Stream, Decoder, Encoder, Profile } from '@garmin/fitsdk';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
|
+
|
|
7
|
+
// src/adapters/shared/message-numbers.ts
|
|
8
|
+
var FIT_MESSAGE_NUMBERS = {
|
|
9
|
+
FILE_ID: 0,
|
|
10
|
+
WORKOUT: 26,
|
|
11
|
+
WORKOUT_STEP: 27
|
|
12
|
+
};
|
|
6
13
|
z.enum([
|
|
7
14
|
"device",
|
|
8
15
|
"settings",
|
|
@@ -276,6 +283,49 @@ var fitDurationTypeSchema = z.enum([
|
|
|
276
283
|
"repeatUntilPowerGreaterThan",
|
|
277
284
|
"open"
|
|
278
285
|
]);
|
|
286
|
+
var fitTargetTypeSchema = z.enum([
|
|
287
|
+
"power",
|
|
288
|
+
"heartRate",
|
|
289
|
+
"cadence",
|
|
290
|
+
"speed",
|
|
291
|
+
"swimStroke",
|
|
292
|
+
"open"
|
|
293
|
+
]);
|
|
294
|
+
var fitEquipmentSchema = z.enum([
|
|
295
|
+
"none",
|
|
296
|
+
"swimFins",
|
|
297
|
+
"swimKickboard",
|
|
298
|
+
"swimPaddles",
|
|
299
|
+
"swimPullBuoy",
|
|
300
|
+
"swimSnorkel"
|
|
301
|
+
]);
|
|
302
|
+
|
|
303
|
+
// src/adapters/equipment/equipment.mapper.ts
|
|
304
|
+
var FIT_TO_KRD_EQUIPMENT_MAP = {
|
|
305
|
+
none: "none",
|
|
306
|
+
swimFins: "swim_fins",
|
|
307
|
+
swimKickboard: "swim_kickboard",
|
|
308
|
+
swimPaddles: "swim_paddles",
|
|
309
|
+
swimPullBuoy: "swim_pull_buoy",
|
|
310
|
+
swimSnorkel: "swim_snorkel"
|
|
311
|
+
};
|
|
312
|
+
var KRD_TO_FIT_EQUIPMENT_MAP = Object.fromEntries(
|
|
313
|
+
Object.entries(FIT_TO_KRD_EQUIPMENT_MAP).map(([fit, krd]) => [krd, fit])
|
|
314
|
+
);
|
|
315
|
+
var mapEquipmentToKrd = (fitEquipment) => {
|
|
316
|
+
const result = fitEquipmentSchema.safeParse(fitEquipment);
|
|
317
|
+
if (!result.success) {
|
|
318
|
+
return equipmentSchema.enum.none;
|
|
319
|
+
}
|
|
320
|
+
return FIT_TO_KRD_EQUIPMENT_MAP[result.data] || equipmentSchema.enum.none;
|
|
321
|
+
};
|
|
322
|
+
var mapEquipmentToFit = (krdEquipment) => {
|
|
323
|
+
const result = equipmentSchema.safeParse(krdEquipment);
|
|
324
|
+
if (!result.success) {
|
|
325
|
+
return fitEquipmentSchema.enum.none;
|
|
326
|
+
}
|
|
327
|
+
return KRD_TO_FIT_EQUIPMENT_MAP[result.data] || fitEquipmentSchema.enum.none;
|
|
328
|
+
};
|
|
279
329
|
var convertConditionalDuration = (duration, message) => {
|
|
280
330
|
if (duration.type === durationTypeSchema.enum.heart_rate_less_than) {
|
|
281
331
|
message.durationType = fitDurationTypeSchema.enum.hrLessThan;
|
|
@@ -371,16 +421,6 @@ var convertDuration = (step, message) => {
|
|
|
371
421
|
if (convertRepeatHrPowerDuration(duration, message)) return;
|
|
372
422
|
message.durationType = fitDurationTypeSchema.enum.open;
|
|
373
423
|
};
|
|
374
|
-
var fitTargetTypeSchema = z.enum([
|
|
375
|
-
"power",
|
|
376
|
-
"heartRate",
|
|
377
|
-
"cadence",
|
|
378
|
-
"speed",
|
|
379
|
-
"swimStroke",
|
|
380
|
-
"open"
|
|
381
|
-
]);
|
|
382
|
-
|
|
383
|
-
// src/adapters/krd-to-fit/krd-to-fit-target-cadence.mapper.ts
|
|
384
424
|
var convertCadenceTarget = (step, message) => {
|
|
385
425
|
message.targetType = fitTargetTypeSchema.enum.cadence;
|
|
386
426
|
if (step.target.type !== targetTypeSchema.enum.cadence) return;
|
|
@@ -468,41 +508,6 @@ var convertTarget = (step, message) => {
|
|
|
468
508
|
convertStrokeTarget(step, message);
|
|
469
509
|
}
|
|
470
510
|
};
|
|
471
|
-
var fitEquipmentSchema = z.enum([
|
|
472
|
-
"none",
|
|
473
|
-
"swimFins",
|
|
474
|
-
"swimKickboard",
|
|
475
|
-
"swimPaddles",
|
|
476
|
-
"swimPullBuoy",
|
|
477
|
-
"swimSnorkel"
|
|
478
|
-
]);
|
|
479
|
-
|
|
480
|
-
// src/adapters/equipment/equipment.mapper.ts
|
|
481
|
-
var FIT_TO_KRD_EQUIPMENT_MAP = {
|
|
482
|
-
none: "none",
|
|
483
|
-
swimFins: "swim_fins",
|
|
484
|
-
swimKickboard: "swim_kickboard",
|
|
485
|
-
swimPaddles: "swim_paddles",
|
|
486
|
-
swimPullBuoy: "swim_pull_buoy",
|
|
487
|
-
swimSnorkel: "swim_snorkel"
|
|
488
|
-
};
|
|
489
|
-
var KRD_TO_FIT_EQUIPMENT_MAP = Object.fromEntries(
|
|
490
|
-
Object.entries(FIT_TO_KRD_EQUIPMENT_MAP).map(([fit, krd]) => [krd, fit])
|
|
491
|
-
);
|
|
492
|
-
var mapEquipmentToKrd = (fitEquipment) => {
|
|
493
|
-
const result = fitEquipmentSchema.safeParse(fitEquipment);
|
|
494
|
-
if (!result.success) {
|
|
495
|
-
return equipmentSchema.enum.none;
|
|
496
|
-
}
|
|
497
|
-
return FIT_TO_KRD_EQUIPMENT_MAP[result.data] || equipmentSchema.enum.none;
|
|
498
|
-
};
|
|
499
|
-
var mapEquipmentToFit = (krdEquipment) => {
|
|
500
|
-
const result = equipmentSchema.safeParse(krdEquipment);
|
|
501
|
-
if (!result.success) {
|
|
502
|
-
return fitEquipmentSchema.enum.none;
|
|
503
|
-
}
|
|
504
|
-
return KRD_TO_FIT_EQUIPMENT_MAP[result.data] || fitEquipmentSchema.enum.none;
|
|
505
|
-
};
|
|
506
511
|
|
|
507
512
|
// src/adapters/krd-to-fit/krd-to-fit-step.mapper.ts
|
|
508
513
|
var FIT_NOTES_MAX_LENGTH = 256;
|
|
@@ -549,13 +554,6 @@ var convertNotes = (notes, stepIndex, behavior, logger) => {
|
|
|
549
554
|
return notes.substring(0, FIT_NOTES_MAX_LENGTH);
|
|
550
555
|
};
|
|
551
556
|
|
|
552
|
-
// src/adapters/shared/message-numbers.ts
|
|
553
|
-
var FIT_MESSAGE_NUMBERS = {
|
|
554
|
-
FILE_ID: 0,
|
|
555
|
-
WORKOUT: 26,
|
|
556
|
-
WORKOUT_STEP: 27
|
|
557
|
-
};
|
|
558
|
-
|
|
559
557
|
// src/adapters/krd-to-fit/krd-to-fit-workout.mapper.ts
|
|
560
558
|
var convertWorkoutSteps = (workout, logger, options = {}) => {
|
|
561
559
|
logger.debug("Converting workout steps", { stepCount: workout.steps.length });
|
|
@@ -666,6 +664,15 @@ var validateCoordinates = (latSemicircles, lonSemicircles) => {
|
|
|
666
664
|
const degreesLon = semicirclesToDegrees(lonSemicircles);
|
|
667
665
|
return degreesLat >= -90 && degreesLat <= 90 && degreesLon >= -180 && degreesLon <= 180;
|
|
668
666
|
};
|
|
667
|
+
var fitMessageKeySchema = z.enum([
|
|
668
|
+
"fileIdMesgs",
|
|
669
|
+
"workoutMesgs",
|
|
670
|
+
"workoutStepMesgs",
|
|
671
|
+
"sessionMesgs",
|
|
672
|
+
"recordMesgs",
|
|
673
|
+
"eventMesgs",
|
|
674
|
+
"lapMesgs"
|
|
675
|
+
]);
|
|
669
676
|
|
|
670
677
|
// src/adapters/event/event-type-maps.ts
|
|
671
678
|
var FIT_EVENT_TO_KRD_TYPE = {
|
|
@@ -795,15 +802,34 @@ var convertFitToKrdEvent = (data) => {
|
|
|
795
802
|
var convertFitToKrdEvents = (events) => {
|
|
796
803
|
return events.map(convertFitToKrdEvent);
|
|
797
804
|
};
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
805
|
+
|
|
806
|
+
// src/adapters/extensions/developer-fields.extractor.ts
|
|
807
|
+
var extractDeveloperFields = (message) => {
|
|
808
|
+
const devFields = [];
|
|
809
|
+
for (const [key, value] of Object.entries(message)) {
|
|
810
|
+
if (key.startsWith("developer_") || key.includes("DeveloperField")) {
|
|
811
|
+
devFields.push({
|
|
812
|
+
fieldName: key,
|
|
813
|
+
value
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return devFields;
|
|
818
|
+
};
|
|
819
|
+
var extractFieldsFromMessageArray = (messages) => {
|
|
820
|
+
const developerFields = [];
|
|
821
|
+
for (const message of messages) {
|
|
822
|
+
if (message && typeof message === "object") {
|
|
823
|
+
const devFields = extractDeveloperFields(
|
|
824
|
+
message
|
|
825
|
+
);
|
|
826
|
+
if (devFields.length > 0) {
|
|
827
|
+
developerFields.push(...devFields);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
return developerFields;
|
|
832
|
+
};
|
|
807
833
|
|
|
808
834
|
// src/adapters/extensions/extensions.extractor.ts
|
|
809
835
|
var extractFitExtensions = (messages, logger) => {
|
|
@@ -831,30 +857,11 @@ var extractAllDeveloperFields = (messages) => {
|
|
|
831
857
|
const developerFields = [];
|
|
832
858
|
for (const value of Object.values(messages)) {
|
|
833
859
|
if (value && Array.isArray(value)) {
|
|
834
|
-
|
|
835
|
-
if (message && typeof message === "object") {
|
|
836
|
-
const devFields = extractDeveloperFields(message);
|
|
837
|
-
if (devFields.length > 0) {
|
|
838
|
-
developerFields.push(...devFields);
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
}
|
|
860
|
+
developerFields.push(...extractFieldsFromMessageArray(value));
|
|
842
861
|
}
|
|
843
862
|
}
|
|
844
863
|
return developerFields;
|
|
845
864
|
};
|
|
846
|
-
var extractDeveloperFields = (message) => {
|
|
847
|
-
const devFields = [];
|
|
848
|
-
for (const [key, value] of Object.entries(message)) {
|
|
849
|
-
if (key.startsWith("developer_") || key.includes("DeveloperField")) {
|
|
850
|
-
devFields.push({
|
|
851
|
-
fieldName: key,
|
|
852
|
-
value
|
|
853
|
-
});
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
return devFields;
|
|
857
|
-
};
|
|
858
865
|
var buildExtensions = (unknownMessages, developerFields, logger) => {
|
|
859
866
|
const extensions = {};
|
|
860
867
|
if (developerFields.length > 0) {
|
|
@@ -871,64 +878,6 @@ var buildExtensions = (unknownMessages, developerFields, logger) => {
|
|
|
871
878
|
}
|
|
872
879
|
return extensions;
|
|
873
880
|
};
|
|
874
|
-
|
|
875
|
-
// src/adapters/lap/lap-trigger.mapper.ts
|
|
876
|
-
var mapFitLapTriggerToKrd = (fit) => {
|
|
877
|
-
switch (fit) {
|
|
878
|
-
case "manual":
|
|
879
|
-
return "manual";
|
|
880
|
-
case "time":
|
|
881
|
-
return "time";
|
|
882
|
-
case "distance":
|
|
883
|
-
return "distance";
|
|
884
|
-
case "positionStart":
|
|
885
|
-
case "positionLap":
|
|
886
|
-
case "positionWaypoint":
|
|
887
|
-
case "positionMarked":
|
|
888
|
-
return "position";
|
|
889
|
-
case "sessionEnd":
|
|
890
|
-
return "session_end";
|
|
891
|
-
case "fitnessEquipment":
|
|
892
|
-
return "fitness_equipment";
|
|
893
|
-
}
|
|
894
|
-
};
|
|
895
|
-
|
|
896
|
-
// src/adapters/lap/lap.mapper.ts
|
|
897
|
-
var mapFitLapToKrd = (fit) => ({
|
|
898
|
-
// Timing - convert ms to seconds
|
|
899
|
-
startTime: new Date(fit.startTime * 1e3).toISOString(),
|
|
900
|
-
totalElapsedTime: fit.totalElapsedTime / 1e3,
|
|
901
|
-
totalTimerTime: fit.totalTimerTime / 1e3,
|
|
902
|
-
// Distance
|
|
903
|
-
totalDistance: fit.totalDistance,
|
|
904
|
-
// Heart rate
|
|
905
|
-
avgHeartRate: fit.avgHeartRate,
|
|
906
|
-
maxHeartRate: fit.maxHeartRate,
|
|
907
|
-
// Cadence
|
|
908
|
-
avgCadence: fit.avgCadence,
|
|
909
|
-
maxCadence: fit.maxCadence,
|
|
910
|
-
// Power
|
|
911
|
-
avgPower: fit.avgPower,
|
|
912
|
-
maxPower: fit.maxPower,
|
|
913
|
-
normalizedPower: fit.normalizedPower,
|
|
914
|
-
// Speed - prefer enhanced values
|
|
915
|
-
avgSpeed: fit.enhancedAvgSpeed ?? fit.avgSpeed,
|
|
916
|
-
maxSpeed: fit.enhancedMaxSpeed ?? fit.maxSpeed,
|
|
917
|
-
// Elevation
|
|
918
|
-
totalAscent: fit.totalAscent,
|
|
919
|
-
totalDescent: fit.totalDescent,
|
|
920
|
-
// Calories
|
|
921
|
-
totalCalories: fit.totalCalories,
|
|
922
|
-
// Classification
|
|
923
|
-
trigger: fit.lapTrigger ? mapFitLapTriggerToKrd(fit.lapTrigger) : void 0,
|
|
924
|
-
sport: fit.sport,
|
|
925
|
-
subSport: fit.subSport ? mapSubSportToKrd(fit.subSport) : void 0,
|
|
926
|
-
// Workout reference
|
|
927
|
-
workoutStepIndex: fit.wktStepIndex,
|
|
928
|
-
// Swimming
|
|
929
|
-
numLengths: fit.numLengths,
|
|
930
|
-
swimStroke: fit.swimStroke !== void 0 ? FIT_TO_SWIM_STROKE[fit.swimStroke] : void 0
|
|
931
|
-
});
|
|
932
881
|
var fitLapTriggerSchema = z.enum([
|
|
933
882
|
"manual",
|
|
934
883
|
"time",
|
|
@@ -989,6 +938,64 @@ var fitLapSchema = z.object({
|
|
|
989
938
|
wktStepIndex: z.number().optional()
|
|
990
939
|
});
|
|
991
940
|
|
|
941
|
+
// src/adapters/lap/lap-trigger.mapper.ts
|
|
942
|
+
var mapFitLapTriggerToKrd = (fit) => {
|
|
943
|
+
switch (fit) {
|
|
944
|
+
case "manual":
|
|
945
|
+
return "manual";
|
|
946
|
+
case "time":
|
|
947
|
+
return "time";
|
|
948
|
+
case "distance":
|
|
949
|
+
return "distance";
|
|
950
|
+
case "positionStart":
|
|
951
|
+
case "positionLap":
|
|
952
|
+
case "positionWaypoint":
|
|
953
|
+
case "positionMarked":
|
|
954
|
+
return "position";
|
|
955
|
+
case "sessionEnd":
|
|
956
|
+
return "session_end";
|
|
957
|
+
case "fitnessEquipment":
|
|
958
|
+
return "fitness_equipment";
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
// src/adapters/lap/fit-to-krd-lap.mapper.ts
|
|
963
|
+
var mapFitLapToKrd = (fit) => ({
|
|
964
|
+
// Timing - convert ms to seconds
|
|
965
|
+
startTime: new Date(fit.startTime * 1e3).toISOString(),
|
|
966
|
+
totalElapsedTime: fit.totalElapsedTime / 1e3,
|
|
967
|
+
totalTimerTime: fit.totalTimerTime / 1e3,
|
|
968
|
+
// Distance
|
|
969
|
+
totalDistance: fit.totalDistance,
|
|
970
|
+
// Heart rate
|
|
971
|
+
avgHeartRate: fit.avgHeartRate,
|
|
972
|
+
maxHeartRate: fit.maxHeartRate,
|
|
973
|
+
// Cadence
|
|
974
|
+
avgCadence: fit.avgCadence,
|
|
975
|
+
maxCadence: fit.maxCadence,
|
|
976
|
+
// Power
|
|
977
|
+
avgPower: fit.avgPower,
|
|
978
|
+
maxPower: fit.maxPower,
|
|
979
|
+
normalizedPower: fit.normalizedPower,
|
|
980
|
+
// Speed - prefer enhanced values
|
|
981
|
+
avgSpeed: fit.enhancedAvgSpeed ?? fit.avgSpeed,
|
|
982
|
+
maxSpeed: fit.enhancedMaxSpeed ?? fit.maxSpeed,
|
|
983
|
+
// Elevation
|
|
984
|
+
totalAscent: fit.totalAscent,
|
|
985
|
+
totalDescent: fit.totalDescent,
|
|
986
|
+
// Calories
|
|
987
|
+
totalCalories: fit.totalCalories,
|
|
988
|
+
// Classification
|
|
989
|
+
trigger: fit.lapTrigger ? mapFitLapTriggerToKrd(fit.lapTrigger) : void 0,
|
|
990
|
+
sport: fit.sport,
|
|
991
|
+
subSport: fit.subSport ? mapSubSportToKrd(fit.subSport) : void 0,
|
|
992
|
+
// Workout reference
|
|
993
|
+
workoutStepIndex: fit.wktStepIndex,
|
|
994
|
+
// Swimming
|
|
995
|
+
numLengths: fit.numLengths,
|
|
996
|
+
swimStroke: fit.swimStroke !== void 0 ? FIT_TO_SWIM_STROKE[fit.swimStroke] : void 0
|
|
997
|
+
});
|
|
998
|
+
|
|
992
999
|
// src/adapters/lap/fit-to-krd-lap.converter.ts
|
|
993
1000
|
var convertFitToKrdLap = (data) => {
|
|
994
1001
|
const fitLap = fitLapSchema.parse(data);
|
|
@@ -997,18 +1004,38 @@ var convertFitToKrdLap = (data) => {
|
|
|
997
1004
|
var convertFitToKrdLaps = (laps) => {
|
|
998
1005
|
return laps.map(convertFitToKrdLap);
|
|
999
1006
|
};
|
|
1007
|
+
var fitRecordSchema = z.object({
|
|
1008
|
+
timestamp: z.number(),
|
|
1009
|
+
positionLat: z.number().optional(),
|
|
1010
|
+
positionLong: z.number().optional(),
|
|
1011
|
+
altitude: z.number().optional(),
|
|
1012
|
+
enhancedAltitude: z.number().optional(),
|
|
1013
|
+
speed: z.number().optional(),
|
|
1014
|
+
enhancedSpeed: z.number().optional(),
|
|
1015
|
+
distance: z.number().optional(),
|
|
1016
|
+
heartRate: z.number().optional(),
|
|
1017
|
+
cadence: z.number().optional(),
|
|
1018
|
+
fractionalCadence: z.number().optional(),
|
|
1019
|
+
power: z.number().optional(),
|
|
1020
|
+
temperature: z.number().optional(),
|
|
1021
|
+
verticalOscillation: z.number().optional(),
|
|
1022
|
+
stanceTime: z.number().optional(),
|
|
1023
|
+
stanceTimePercent: z.number().optional(),
|
|
1024
|
+
stepLength: z.number().optional(),
|
|
1025
|
+
compressedTimestamp: z.number().optional()
|
|
1026
|
+
});
|
|
1000
1027
|
|
|
1001
|
-
// src/adapters/record/record.mapper.ts
|
|
1002
|
-
var
|
|
1003
|
-
const record = {
|
|
1004
|
-
timestamp: new Date(fit.timestamp * 1e3).toISOString()
|
|
1005
|
-
};
|
|
1028
|
+
// src/adapters/record/record-from-fit.mapper.ts
|
|
1029
|
+
var mapFitPosition = (fit) => {
|
|
1006
1030
|
if (fit.positionLat !== void 0 && fit.positionLong !== void 0) {
|
|
1007
|
-
|
|
1031
|
+
return {
|
|
1008
1032
|
lat: semicirclesToDegrees(fit.positionLat),
|
|
1009
1033
|
lon: semicirclesToDegrees(fit.positionLong)
|
|
1010
1034
|
};
|
|
1011
1035
|
}
|
|
1036
|
+
return void 0;
|
|
1037
|
+
};
|
|
1038
|
+
var mapFitAltitudeAndSpeed = (fit, record) => {
|
|
1012
1039
|
if (fit.enhancedAltitude !== void 0) {
|
|
1013
1040
|
record.altitude = fit.enhancedAltitude;
|
|
1014
1041
|
} else if (fit.altitude !== void 0) {
|
|
@@ -1019,6 +1046,8 @@ var mapFitRecordToKrd = (fit) => {
|
|
|
1019
1046
|
} else if (fit.speed !== void 0) {
|
|
1020
1047
|
record.speed = fit.speed;
|
|
1021
1048
|
}
|
|
1049
|
+
};
|
|
1050
|
+
var mapFitCoreFields = (fit, record) => {
|
|
1022
1051
|
if (fit.distance !== void 0) record.distance = fit.distance;
|
|
1023
1052
|
if (fit.heartRate !== void 0) record.heartRate = fit.heartRate;
|
|
1024
1053
|
if (fit.power !== void 0) record.power = fit.power;
|
|
@@ -1026,33 +1055,25 @@ var mapFitRecordToKrd = (fit) => {
|
|
|
1026
1055
|
if (fit.cadence !== void 0) {
|
|
1027
1056
|
record.cadence = fit.cadence + (fit.fractionalCadence ?? 0);
|
|
1028
1057
|
}
|
|
1058
|
+
};
|
|
1059
|
+
var mapFitRunningDynamics = (fit, record) => {
|
|
1029
1060
|
if (fit.verticalOscillation !== void 0) {
|
|
1030
1061
|
record.verticalOscillation = fit.verticalOscillation;
|
|
1031
1062
|
}
|
|
1032
1063
|
if (fit.stanceTime !== void 0) record.stanceTime = fit.stanceTime;
|
|
1033
1064
|
if (fit.stepLength !== void 0) record.stepLength = fit.stepLength;
|
|
1065
|
+
};
|
|
1066
|
+
var mapFitRecordToKrd = (fit) => {
|
|
1067
|
+
const record = {
|
|
1068
|
+
timestamp: new Date(fit.timestamp * 1e3).toISOString()
|
|
1069
|
+
};
|
|
1070
|
+
const position = mapFitPosition(fit);
|
|
1071
|
+
if (position) record.position = position;
|
|
1072
|
+
mapFitAltitudeAndSpeed(fit, record);
|
|
1073
|
+
mapFitCoreFields(fit, record);
|
|
1074
|
+
mapFitRunningDynamics(fit, record);
|
|
1034
1075
|
return record;
|
|
1035
1076
|
};
|
|
1036
|
-
var fitRecordSchema = z.object({
|
|
1037
|
-
timestamp: z.number(),
|
|
1038
|
-
positionLat: z.number().optional(),
|
|
1039
|
-
positionLong: z.number().optional(),
|
|
1040
|
-
altitude: z.number().optional(),
|
|
1041
|
-
enhancedAltitude: z.number().optional(),
|
|
1042
|
-
speed: z.number().optional(),
|
|
1043
|
-
enhancedSpeed: z.number().optional(),
|
|
1044
|
-
distance: z.number().optional(),
|
|
1045
|
-
heartRate: z.number().optional(),
|
|
1046
|
-
cadence: z.number().optional(),
|
|
1047
|
-
fractionalCadence: z.number().optional(),
|
|
1048
|
-
power: z.number().optional(),
|
|
1049
|
-
temperature: z.number().optional(),
|
|
1050
|
-
verticalOscillation: z.number().optional(),
|
|
1051
|
-
stanceTime: z.number().optional(),
|
|
1052
|
-
stanceTimePercent: z.number().optional(),
|
|
1053
|
-
stepLength: z.number().optional(),
|
|
1054
|
-
compressedTimestamp: z.number().optional()
|
|
1055
|
-
});
|
|
1056
1077
|
|
|
1057
1078
|
// src/adapters/record/fit-to-krd-record.converter.ts
|
|
1058
1079
|
var convertFitToKrdRecord = (data) => {
|
|
@@ -1072,30 +1093,6 @@ var convertFitToKrdRecord = (data) => {
|
|
|
1072
1093
|
var convertFitToKrdRecords = (records) => {
|
|
1073
1094
|
return records.map(convertFitToKrdRecord);
|
|
1074
1095
|
};
|
|
1075
|
-
|
|
1076
|
-
// src/adapters/session/session.mapper.ts
|
|
1077
|
-
var mapFitSessionToKrd = (fit) => ({
|
|
1078
|
-
startTime: new Date(fit.startTime * 1e3).toISOString(),
|
|
1079
|
-
totalElapsedTime: fit.totalElapsedTime / 1e3,
|
|
1080
|
-
totalTimerTime: fit.totalTimerTime !== void 0 ? fit.totalTimerTime / 1e3 : void 0,
|
|
1081
|
-
totalDistance: fit.totalDistance,
|
|
1082
|
-
sport: String(fit.sport),
|
|
1083
|
-
subSport: fit.subSport ? mapSubSportToKrd(fit.subSport) : void 0,
|
|
1084
|
-
avgHeartRate: fit.avgHeartRate,
|
|
1085
|
-
maxHeartRate: fit.maxHeartRate,
|
|
1086
|
-
avgCadence: fit.avgCadence,
|
|
1087
|
-
maxCadence: fit.maxCadence,
|
|
1088
|
-
avgPower: fit.avgPower,
|
|
1089
|
-
maxPower: fit.maxPower,
|
|
1090
|
-
normalizedPower: fit.normalizedPower,
|
|
1091
|
-
trainingStressScore: fit.trainingStressScore,
|
|
1092
|
-
intensityFactor: fit.intensityFactor,
|
|
1093
|
-
totalCalories: fit.totalCalories,
|
|
1094
|
-
totalAscent: fit.totalAscent,
|
|
1095
|
-
totalDescent: fit.totalDescent,
|
|
1096
|
-
avgSpeed: fit.enhancedAvgSpeed ?? fit.avgSpeed,
|
|
1097
|
-
maxSpeed: fit.enhancedMaxSpeed ?? fit.maxSpeed
|
|
1098
|
-
});
|
|
1099
1096
|
var fitSessionSchema = z.object({
|
|
1100
1097
|
timestamp: z.number(),
|
|
1101
1098
|
startTime: z.number(),
|
|
@@ -1124,6 +1121,30 @@ var fitSessionSchema = z.object({
|
|
|
1124
1121
|
firstLapIndex: z.number().optional()
|
|
1125
1122
|
});
|
|
1126
1123
|
|
|
1124
|
+
// src/adapters/session/session.mapper.ts
|
|
1125
|
+
var mapFitSessionToKrd = (fit) => ({
|
|
1126
|
+
startTime: new Date(fit.startTime * 1e3).toISOString(),
|
|
1127
|
+
totalElapsedTime: fit.totalElapsedTime / 1e3,
|
|
1128
|
+
totalTimerTime: fit.totalTimerTime !== void 0 ? fit.totalTimerTime / 1e3 : void 0,
|
|
1129
|
+
totalDistance: fit.totalDistance,
|
|
1130
|
+
sport: String(fit.sport),
|
|
1131
|
+
subSport: fit.subSport ? mapSubSportToKrd(fit.subSport) : void 0,
|
|
1132
|
+
avgHeartRate: fit.avgHeartRate,
|
|
1133
|
+
maxHeartRate: fit.maxHeartRate,
|
|
1134
|
+
avgCadence: fit.avgCadence,
|
|
1135
|
+
maxCadence: fit.maxCadence,
|
|
1136
|
+
avgPower: fit.avgPower,
|
|
1137
|
+
maxPower: fit.maxPower,
|
|
1138
|
+
normalizedPower: fit.normalizedPower,
|
|
1139
|
+
trainingStressScore: fit.trainingStressScore,
|
|
1140
|
+
intensityFactor: fit.intensityFactor,
|
|
1141
|
+
totalCalories: fit.totalCalories,
|
|
1142
|
+
totalAscent: fit.totalAscent,
|
|
1143
|
+
totalDescent: fit.totalDescent,
|
|
1144
|
+
avgSpeed: fit.enhancedAvgSpeed ?? fit.avgSpeed,
|
|
1145
|
+
maxSpeed: fit.enhancedMaxSpeed ?? fit.maxSpeed
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1127
1148
|
// src/adapters/session/fit-to-krd-session.converter.ts
|
|
1128
1149
|
var convertFitToKrdSession = (data) => {
|
|
1129
1150
|
const fitSession = fitSessionSchema.parse(data);
|
|
@@ -1138,6 +1159,13 @@ var convertTimeCreated = (timeCreated) => {
|
|
|
1138
1159
|
return new Date(timeCreated * 1e3).toISOString();
|
|
1139
1160
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1140
1161
|
};
|
|
1162
|
+
var buildKrdMetadata = (fileId, session) => ({
|
|
1163
|
+
created: convertTimeCreated(fileId ? fileId.timeCreated : void 0),
|
|
1164
|
+
sport: (session ? session.sport : void 0) ?? "other",
|
|
1165
|
+
subSport: session ? session.subSport : void 0
|
|
1166
|
+
});
|
|
1167
|
+
var toOptionalArray = (items) => items.length > 0 ? items : void 0;
|
|
1168
|
+
var toOptionalSingle = (item) => item !== void 0 ? [item] : void 0;
|
|
1141
1169
|
var mapActivityFileToKRD = (messages, logger) => {
|
|
1142
1170
|
const fileId = messages[fitMessageKeySchema.enum.fileIdMesgs]?.[0];
|
|
1143
1171
|
const sessionMsgs = messages[fitMessageKeySchema.enum.sessionMesgs] || [];
|
|
@@ -1155,47 +1183,20 @@ var mapActivityFileToKRD = (messages, logger) => {
|
|
|
1155
1183
|
const events = convertFitToKrdEvents(eventMsgs);
|
|
1156
1184
|
const laps = convertFitToKrdLaps(lapMsgs);
|
|
1157
1185
|
const fitExtensions = extractFitExtensions(messages, logger);
|
|
1158
|
-
const created = convertTimeCreated(fileId?.timeCreated);
|
|
1159
1186
|
return {
|
|
1160
1187
|
version: KRD_VERSION,
|
|
1161
1188
|
type: fileTypeSchema.enum.recorded_activity,
|
|
1162
|
-
metadata:
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
extensions: {
|
|
1172
|
-
fit: fitExtensions
|
|
1173
|
-
}
|
|
1189
|
+
metadata: buildKrdMetadata(
|
|
1190
|
+
fileId,
|
|
1191
|
+
session
|
|
1192
|
+
),
|
|
1193
|
+
sessions: toOptionalSingle(session),
|
|
1194
|
+
laps: toOptionalArray(laps),
|
|
1195
|
+
records: toOptionalArray(records),
|
|
1196
|
+
events: toOptionalArray(events),
|
|
1197
|
+
extensions: { fit: fitExtensions }
|
|
1174
1198
|
};
|
|
1175
1199
|
};
|
|
1176
|
-
var validateMessages = (fileId, workoutMsg, messages, logger, options = {}) => {
|
|
1177
|
-
const { strict = true } = options;
|
|
1178
|
-
if (!fileId) {
|
|
1179
|
-
const message = "Missing required fileId message in FIT file";
|
|
1180
|
-
if (strict) {
|
|
1181
|
-
throw createFitParsingError(message);
|
|
1182
|
-
}
|
|
1183
|
-
logger.warn(message);
|
|
1184
|
-
}
|
|
1185
|
-
if (!workoutMsg) {
|
|
1186
|
-
const message = "Missing required workout message in FIT file";
|
|
1187
|
-
if (strict) {
|
|
1188
|
-
throw createFitParsingError(message);
|
|
1189
|
-
}
|
|
1190
|
-
logger.warn(message);
|
|
1191
|
-
}
|
|
1192
|
-
const workoutMessages = messages[fitMessageKeySchema.enum.workoutMesgs];
|
|
1193
|
-
if (workoutMessages && workoutMessages.length > 1) {
|
|
1194
|
-
logger.warn("Multiple workout messages found, using first one", {
|
|
1195
|
-
count: workoutMessages.length
|
|
1196
|
-
});
|
|
1197
|
-
}
|
|
1198
|
-
};
|
|
1199
1200
|
var DEFAULT_SPORT = sportSchema.enum.cycling;
|
|
1200
1201
|
var mapSportType = (fitSport) => {
|
|
1201
1202
|
if (!fitSport) {
|
|
@@ -1231,6 +1232,16 @@ var mapCreatedTimestamp = (fileId) => {
|
|
|
1231
1232
|
}
|
|
1232
1233
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1233
1234
|
};
|
|
1235
|
+
var FIT_LENGTH_UNIT_MAP = {
|
|
1236
|
+
0: "meters",
|
|
1237
|
+
1: "yards"
|
|
1238
|
+
};
|
|
1239
|
+
var mapLengthUnitToKrd = (fitUnit) => {
|
|
1240
|
+
if (fitUnit === void 0) {
|
|
1241
|
+
return lengthUnitSchema.enum.meters;
|
|
1242
|
+
}
|
|
1243
|
+
return FIT_LENGTH_UNIT_MAP[fitUnit] || lengthUnitSchema.enum.meters;
|
|
1244
|
+
};
|
|
1234
1245
|
var convertTimeDuration = (data) => {
|
|
1235
1246
|
if (data.durationTime !== void 0) {
|
|
1236
1247
|
return {
|
|
@@ -1388,34 +1399,24 @@ var convertFitDuration = (data) => {
|
|
|
1388
1399
|
var mapDuration = (step) => {
|
|
1389
1400
|
return convertFitDuration(step);
|
|
1390
1401
|
};
|
|
1402
|
+
var FIT_TO_KRD_DURATION_TYPE = {
|
|
1403
|
+
[fitDurationTypeSchema.enum.time]: durationTypeSchema.enum.time,
|
|
1404
|
+
[fitDurationTypeSchema.enum.distance]: durationTypeSchema.enum.distance,
|
|
1405
|
+
[fitDurationTypeSchema.enum.hrLessThan]: durationTypeSchema.enum.heart_rate_less_than,
|
|
1406
|
+
[fitDurationTypeSchema.enum.repeatUntilHrGreaterThan]: durationTypeSchema.enum.repeat_until_heart_rate_greater_than,
|
|
1407
|
+
[fitDurationTypeSchema.enum.calories]: durationTypeSchema.enum.calories,
|
|
1408
|
+
[fitDurationTypeSchema.enum.powerLessThan]: durationTypeSchema.enum.power_less_than,
|
|
1409
|
+
[fitDurationTypeSchema.enum.powerGreaterThan]: durationTypeSchema.enum.power_greater_than,
|
|
1410
|
+
[fitDurationTypeSchema.enum.repeatUntilTime]: durationTypeSchema.enum.repeat_until_time,
|
|
1411
|
+
[fitDurationTypeSchema.enum.repeatUntilDistance]: durationTypeSchema.enum.repeat_until_distance,
|
|
1412
|
+
[fitDurationTypeSchema.enum.repeatUntilCalories]: durationTypeSchema.enum.repeat_until_calories,
|
|
1413
|
+
[fitDurationTypeSchema.enum.repeatUntilHrLessThan]: durationTypeSchema.enum.repeat_until_heart_rate_less_than,
|
|
1414
|
+
[fitDurationTypeSchema.enum.repeatUntilPowerLessThan]: durationTypeSchema.enum.repeat_until_power_less_than,
|
|
1415
|
+
[fitDurationTypeSchema.enum.repeatUntilPowerGreaterThan]: durationTypeSchema.enum.repeat_until_power_greater_than
|
|
1416
|
+
};
|
|
1391
1417
|
var mapDurationType = (fitDurationType) => {
|
|
1392
|
-
if (fitDurationType
|
|
1393
|
-
|
|
1394
|
-
if (fitDurationType === fitDurationTypeSchema.enum.distance)
|
|
1395
|
-
return durationTypeSchema.enum.distance;
|
|
1396
|
-
if (fitDurationType === fitDurationTypeSchema.enum.hrLessThan)
|
|
1397
|
-
return durationTypeSchema.enum.heart_rate_less_than;
|
|
1398
|
-
if (fitDurationType === fitDurationTypeSchema.enum.repeatUntilHrGreaterThan)
|
|
1399
|
-
return durationTypeSchema.enum.repeat_until_heart_rate_greater_than;
|
|
1400
|
-
if (fitDurationType === fitDurationTypeSchema.enum.calories)
|
|
1401
|
-
return durationTypeSchema.enum.calories;
|
|
1402
|
-
if (fitDurationType === fitDurationTypeSchema.enum.powerLessThan)
|
|
1403
|
-
return durationTypeSchema.enum.power_less_than;
|
|
1404
|
-
if (fitDurationType === fitDurationTypeSchema.enum.powerGreaterThan)
|
|
1405
|
-
return durationTypeSchema.enum.power_greater_than;
|
|
1406
|
-
if (fitDurationType === fitDurationTypeSchema.enum.repeatUntilTime)
|
|
1407
|
-
return durationTypeSchema.enum.repeat_until_time;
|
|
1408
|
-
if (fitDurationType === fitDurationTypeSchema.enum.repeatUntilDistance)
|
|
1409
|
-
return durationTypeSchema.enum.repeat_until_distance;
|
|
1410
|
-
if (fitDurationType === fitDurationTypeSchema.enum.repeatUntilCalories)
|
|
1411
|
-
return durationTypeSchema.enum.repeat_until_calories;
|
|
1412
|
-
if (fitDurationType === fitDurationTypeSchema.enum.repeatUntilHrLessThan)
|
|
1413
|
-
return durationTypeSchema.enum.repeat_until_heart_rate_less_than;
|
|
1414
|
-
if (fitDurationType === fitDurationTypeSchema.enum.repeatUntilPowerLessThan)
|
|
1415
|
-
return durationTypeSchema.enum.repeat_until_power_less_than;
|
|
1416
|
-
if (fitDurationType === fitDurationTypeSchema.enum.repeatUntilPowerGreaterThan)
|
|
1417
|
-
return durationTypeSchema.enum.repeat_until_power_greater_than;
|
|
1418
|
-
return durationTypeSchema.enum.open;
|
|
1418
|
+
if (!fitDurationType) return durationTypeSchema.enum.open;
|
|
1419
|
+
return FIT_TO_KRD_DURATION_TYPE[fitDurationType] ?? durationTypeSchema.enum.open;
|
|
1419
1420
|
};
|
|
1420
1421
|
var convertCadenceTarget2 = (data) => {
|
|
1421
1422
|
const rangeTarget = buildCadenceRangeTarget(data);
|
|
@@ -1822,16 +1823,6 @@ var buildRepetitionBlock = (step, workoutSteps, currentIndex) => {
|
|
|
1822
1823
|
steps: repeatedSteps
|
|
1823
1824
|
};
|
|
1824
1825
|
};
|
|
1825
|
-
var FIT_LENGTH_UNIT_MAP = {
|
|
1826
|
-
0: "meters",
|
|
1827
|
-
1: "yards"
|
|
1828
|
-
};
|
|
1829
|
-
var mapLengthUnitToKrd = (fitUnit) => {
|
|
1830
|
-
if (fitUnit === void 0) {
|
|
1831
|
-
return lengthUnitSchema.enum.meters;
|
|
1832
|
-
}
|
|
1833
|
-
return FIT_LENGTH_UNIT_MAP[fitUnit] || lengthUnitSchema.enum.meters;
|
|
1834
|
-
};
|
|
1835
1826
|
|
|
1836
1827
|
// src/adapters/workout/workout.mapper.ts
|
|
1837
1828
|
var mapWorkout = (workoutMsg, workoutSteps, logger) => {
|
|
@@ -1855,6 +1846,31 @@ var mapWorkout = (workoutMsg, workoutSteps, logger) => {
|
|
|
1855
1846
|
}
|
|
1856
1847
|
return workout;
|
|
1857
1848
|
};
|
|
1849
|
+
var checkFileId = (fileId, logger, strict) => {
|
|
1850
|
+
if (!fileId) {
|
|
1851
|
+
const message = "Missing required fileId message in FIT file";
|
|
1852
|
+
if (strict) throw createFitParsingError(message);
|
|
1853
|
+
logger.warn(message);
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
var checkWorkoutMsg = (workoutMsg, logger, strict) => {
|
|
1857
|
+
if (!workoutMsg) {
|
|
1858
|
+
const message = "Missing required workout message in FIT file";
|
|
1859
|
+
if (strict) throw createFitParsingError(message);
|
|
1860
|
+
logger.warn(message);
|
|
1861
|
+
}
|
|
1862
|
+
};
|
|
1863
|
+
var validateMessages = (...[fileId, workoutMsg, messages, logger, options = {}]) => {
|
|
1864
|
+
const { strict = true } = options;
|
|
1865
|
+
checkFileId(fileId, logger, strict);
|
|
1866
|
+
checkWorkoutMsg(workoutMsg, logger, strict);
|
|
1867
|
+
const workoutMessages = messages[fitMessageKeySchema.enum.workoutMesgs];
|
|
1868
|
+
if (workoutMessages && workoutMessages.length > 1) {
|
|
1869
|
+
logger.warn("Multiple workout messages found, using first one", {
|
|
1870
|
+
count: workoutMessages.length
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
1858
1874
|
|
|
1859
1875
|
// src/adapters/messages/workout.mapper.ts
|
|
1860
1876
|
var KRD_VERSION2 = "1.0";
|