ts-garmin 0.1.1 → 0.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/README.md CHANGED
@@ -172,6 +172,7 @@ const userId = await garmin.getUserId(tokens.accessToken) // the key push notifi
172
172
  const legacy = new GarminConnectClient()
173
173
  await legacy.login('email@example.com', 'password')
174
174
  const activities = await legacy.getActivities(new Date('2024-01-01'))
175
+ const { bytes, filename } = await legacy.downloadOriginalActivity(activityId) // "Export Original" zip, in memory
175
176
 
176
177
  // Strava
177
178
  const strava = new StravaClient({
@@ -1,5 +1,5 @@
1
1
  import { HttpClient } from '../common/HttpClient';
2
- import type { ActivitySubType, ActivityType, ExportFileTypeValue, GCActivityId, GCGearId, GarminDomain, IActivity, ICountActivities, IGarminTokens, IOauth1Token, IOauth2Token, ISocialProfile, IUserSettings, IWorkout, IWorkoutDetail, ListCoursesResponse, UploadFileTypeValue } from './types/index';
2
+ import type { ActivitySubType, ActivityType, ExportFileTypeValue, GCActivityId, GCGearId, GarminDomain, IActivity, IActivityDetails, ICountActivities, IGarminTokens, IOauth1Token, IOauth2Token, ISocialProfile, IUserSettings, IWorkout, IWorkoutDetail, ListCoursesResponse, UploadFileTypeValue } from './types/index';
3
3
  import type { CoursePoint, GeoPoint, GpxActivityType, ImportedGpxResponse } from './types/gpx';
4
4
  import type { GearData } from './types/gear';
5
5
  import type { HeartRate } from './types/heartrate';
@@ -29,8 +29,10 @@ export default class GarminConnect {
29
29
  getUserProfile(): Promise<ISocialProfile>;
30
30
  getActivities(start?: number, limit?: number, activityType?: ActivityType, subActivityType?: ActivitySubType): Promise<IActivity[]>;
31
31
  getActivity(activity: { activityId: GCActivityId }): Promise<IActivity>;
32
+ getActivityDetails(activity: { activityId: GCActivityId }, maxChartSize?: number): Promise<IActivityDetails>;
32
33
  countActivities(): Promise<ICountActivities>;
33
- downloadOriginalActivityData(activity: { activityId: GCActivityId }, dir: string, type?: ExportFileTypeValue): Promise<void>;
34
+ getOriginalActivityData(activity: { activityId: GCActivityId }, type?: ExportFileTypeValue): Promise<{ bytes: Uint8Array, filename: string }>;
35
+ downloadOriginalActivityData(activity: { activityId: GCActivityId }, dir: string, type?: ExportFileTypeValue): Promise<string>;
34
36
  uploadActivity(file: string, format?: UploadFileTypeValue): Promise<unknown>;
35
37
  deleteActivity(activity: { activityId: GCActivityId }): Promise<void>;
36
38
  getWorkouts(start: number, limit: number): Promise<IWorkout[]>;
@@ -377,6 +377,24 @@ export declare interface IActivityDetails {
377
377
  maxDistance: number
378
378
  }[] | null
379
379
  }
380
+ /** One series in an activity's details, e.g. directHeartRate or sumDuration. */
381
+ export declare interface IActivityDetailMetricDescriptor {
382
+ metricsIndex: number
383
+ key: string
384
+ unit?: { id?: number, key?: string, factor?: number }
385
+ }
386
+ /**
387
+ * An activity's recorded time series from activity-service's details
388
+ * endpoint: `activityDetailMetrics[i].metrics[metricsIndex]` is sample i of
389
+ * the series the descriptor names. Garmin downsamples to `maxChartSize`.
390
+ */
391
+ export declare interface IActivityDetails {
392
+ activityId: number
393
+ measurementCount?: number
394
+ metricsCount?: number
395
+ metricDescriptors: IActivityDetailMetricDescriptor[]
396
+ activityDetailMetrics: Array<{ metrics: Array<number | null> }>
397
+ }
380
398
  export type GCActivityId = number;
381
399
  export declare enum ActivityType {
382
400
  Cycling = 'cycling',
package/dist/index.js CHANGED
@@ -143,13 +143,13 @@ class HttpClient {
143
143
  const text = await response.text();
144
144
  throw new Error(`HTTP Error: ${response.status} ${response.statusText} - ${text}`);
145
145
  }
146
+ if (responseType === "arraybuffer") {
147
+ return response.arrayBuffer();
148
+ }
146
149
  const contentType = response.headers.get("content-type");
147
150
  if (contentType?.includes("application/json")) {
148
151
  return response.json();
149
152
  }
150
- if (responseType === "arraybuffer") {
151
- return response.arrayBuffer();
152
- }
153
153
  return response.text();
154
154
  }
155
155
  async get(url, config) {
@@ -538,54 +538,54 @@ class UrlClass {
538
538
 
539
539
  // src/garmin/types/activity.ts
540
540
  var ActivityType;
541
- ((ActivityType2) => {
542
- ActivityType2["Cycling"] = "cycling";
543
- ActivityType2["FitnessEquipment"] = "fitness_equipment";
544
- ActivityType2["Walking"] = "walking";
545
- ActivityType2["Hiking"] = "hiking";
546
- ActivityType2["Other"] = "other";
547
- ActivityType2["WaterSport"] = "water_sports";
548
- ActivityType2["Running"] = "street_running";
541
+ ((ActivityType) => {
542
+ ActivityType["Cycling"] = "cycling";
543
+ ActivityType["FitnessEquipment"] = "fitness_equipment";
544
+ ActivityType["Walking"] = "walking";
545
+ ActivityType["Hiking"] = "hiking";
546
+ ActivityType["Other"] = "other";
547
+ ActivityType["WaterSport"] = "water_sports";
548
+ ActivityType["Running"] = "street_running";
549
549
  })(ActivityType ||= {});
550
550
  var ActivitySubType;
551
- ((ActivitySubType2) => {
552
- ActivitySubType2["IndoorCardio"] = "indoor_cardio";
553
- ActivitySubType2["StrengthTraining"] = "strength_training";
554
- ActivitySubType2["HIIT"] = "hiit";
555
- ActivitySubType2["Yoga"] = "yoga";
556
- ActivitySubType2["IndoorCycling"] = "indoor_cycling";
557
- ActivitySubType2["Breathwork"] = "breathwork";
558
- ActivitySubType2["Surfing"] = "surfing";
559
- ActivitySubType2["StreetRunning"] = "street_running";
560
- ActivitySubType2["TrailRunning"] = "trail_running";
561
- ActivitySubType2["IndoorRunning"] = "indoor_running";
551
+ ((ActivitySubType) => {
552
+ ActivitySubType["IndoorCardio"] = "indoor_cardio";
553
+ ActivitySubType["StrengthTraining"] = "strength_training";
554
+ ActivitySubType["HIIT"] = "hiit";
555
+ ActivitySubType["Yoga"] = "yoga";
556
+ ActivitySubType["IndoorCycling"] = "indoor_cycling";
557
+ ActivitySubType["Breathwork"] = "breathwork";
558
+ ActivitySubType["Surfing"] = "surfing";
559
+ ActivitySubType["StreetRunning"] = "street_running";
560
+ ActivitySubType["TrailRunning"] = "trail_running";
561
+ ActivitySubType["IndoorRunning"] = "indoor_running";
562
562
  })(ActivitySubType ||= {});
563
563
  // src/garmin/types/gpx.ts
564
564
  var GpxActivityType;
565
- ((GpxActivityType2) => {
566
- GpxActivityType2[GpxActivityType2["Running"] = 1] = "Running";
567
- GpxActivityType2[GpxActivityType2["Running_Road"] = 1] = "Running_Road";
568
- GpxActivityType2[GpxActivityType2["Running_Trail"] = 1] = "Running_Trail";
569
- GpxActivityType2[GpxActivityType2["Cycling"] = 10] = "Cycling";
570
- GpxActivityType2[GpxActivityType2["Cycling_Road"] = 10] = "Cycling_Road";
571
- GpxActivityType2[GpxActivityType2["Cycling_Gravel"] = 143] = "Cycling_Gravel";
572
- GpxActivityType2[GpxActivityType2["Hiking"] = 3] = "Hiking";
573
- GpxActivityType2[GpxActivityType2["Other"] = 4] = "Other";
565
+ ((GpxActivityType) => {
566
+ GpxActivityType[GpxActivityType["Running"] = 1] = "Running";
567
+ GpxActivityType[GpxActivityType["Running_Road"] = 1] = "Running_Road";
568
+ GpxActivityType[GpxActivityType["Running_Trail"] = 1] = "Running_Trail";
569
+ GpxActivityType[GpxActivityType["Cycling"] = 10] = "Cycling";
570
+ GpxActivityType[GpxActivityType["Cycling_Road"] = 10] = "Cycling_Road";
571
+ GpxActivityType[GpxActivityType["Cycling_Gravel"] = 143] = "Cycling_Gravel";
572
+ GpxActivityType[GpxActivityType["Hiking"] = 3] = "Hiking";
573
+ GpxActivityType[GpxActivityType["Other"] = 4] = "Other";
574
574
  })(GpxActivityType ||= {});
575
575
 
576
576
  // src/garmin/types/index.ts
577
577
  var ExportFileType;
578
- ((ExportFileType2) => {
579
- ExportFileType2["tcx"] = "tcx";
580
- ExportFileType2["gpx"] = "gpx";
581
- ExportFileType2["kml"] = "kml";
582
- ExportFileType2["zip"] = "zip";
578
+ ((ExportFileType) => {
579
+ ExportFileType["tcx"] = "tcx";
580
+ ExportFileType["gpx"] = "gpx";
581
+ ExportFileType["kml"] = "kml";
582
+ ExportFileType["zip"] = "zip";
583
583
  })(ExportFileType ||= {});
584
584
  var UploadFileType;
585
- ((UploadFileType2) => {
586
- UploadFileType2["tcx"] = "tcx";
587
- UploadFileType2["gpx"] = "gpx";
588
- UploadFileType2["fit"] = "fit";
585
+ ((UploadFileType) => {
586
+ UploadFileType["tcx"] = "tcx";
587
+ UploadFileType["gpx"] = "gpx";
588
+ UploadFileType["fit"] = "fit";
589
589
  })(UploadFileType ||= {});
590
590
 
591
591
  // src/garmin/common/DateUtils.ts
@@ -720,10 +720,17 @@ class GarminConnect {
720
720
  params: { start, limit, activityType, subActivityType }
721
721
  });
722
722
  }
723
- async getActivity(activity2) {
724
- if (!activity2.activityId)
723
+ async getActivity(activity) {
724
+ if (!activity.activityId)
725
+ throw new Error("Missing activityId");
726
+ return this.client.get(`${this.url.ACTIVITY}${activity.activityId}`);
727
+ }
728
+ async getActivityDetails(activity, maxChartSize = 2000) {
729
+ if (!activity.activityId)
725
730
  throw new Error("Missing activityId");
726
- return this.client.get(`${this.url.ACTIVITY}${activity2.activityId}`);
731
+ return this.client.get(`${this.url.ACTIVITY_BY_ID(activity.activityId)}/details`, {
732
+ params: { maxChartSize, maxPolylineSize: 0, maxHeatMapSize: 0 }
733
+ });
727
734
  }
728
735
  async countActivities() {
729
736
  const now = new Date;
@@ -737,27 +744,32 @@ class GarminConnect {
737
744
  }
738
745
  });
739
746
  }
740
- async downloadOriginalActivityData(activity2, dir, type = "zip") {
741
- if (!activity2.activityId)
747
+ async getOriginalActivityData(activity, type = "zip") {
748
+ if (!activity.activityId)
742
749
  throw new Error("Missing activityId");
743
- if (!checkIsDirectory(dir)) {
744
- createDirectory(dir);
745
- }
746
- let fileBuffer;
747
750
  let url;
748
751
  if (type === "tcx") {
749
- url = `${this.url.DOWNLOAD_TCX}${activity2.activityId}`;
752
+ url = `${this.url.DOWNLOAD_TCX}${activity.activityId}`;
750
753
  } else if (type === "gpx") {
751
- url = `${this.url.DOWNLOAD_GPX}${activity2.activityId}`;
754
+ url = `${this.url.DOWNLOAD_GPX}${activity.activityId}`;
752
755
  } else if (type === "kml") {
753
- url = `${this.url.DOWNLOAD_KML}${activity2.activityId}`;
756
+ url = `${this.url.DOWNLOAD_KML}${activity.activityId}`;
754
757
  } else if (type === "zip") {
755
- url = `${this.url.DOWNLOAD_ZIP}${activity2.activityId}`;
758
+ url = `${this.url.DOWNLOAD_ZIP}${activity.activityId}`;
756
759
  } else {
757
- throw new Error(`downloadOriginalActivityData - Invalid type: ${type}`);
760
+ throw new Error(`getOriginalActivityData - Invalid type: ${type}`);
761
+ }
762
+ const body = await this.client.get(url, { responseType: "arraybuffer" });
763
+ return { bytes: new Uint8Array(body), filename: `${activity.activityId}.${type}` };
764
+ }
765
+ async downloadOriginalActivityData(activity, dir, type = "zip") {
766
+ const { bytes, filename } = await this.getOriginalActivityData(activity, type);
767
+ if (!checkIsDirectory(dir)) {
768
+ createDirectory(dir);
758
769
  }
759
- fileBuffer = await this.client.get(url, { responseType: "arraybuffer" });
760
- writeToFile(path.join(dir, `${activity2.activityId}.${type}`), Buffer.from(fileBuffer));
770
+ const file = path.join(dir, filename);
771
+ writeToFile(file, bytes);
772
+ return file;
761
773
  }
762
774
  async uploadActivity(file, format = "fit") {
763
775
  const detectedFormat = (format || path.extname(file))?.toLowerCase();
@@ -776,31 +788,31 @@ class GarminConnect {
776
788
  });
777
789
  return response.json();
778
790
  }
779
- async deleteActivity(activity2) {
780
- if (!activity2.activityId)
791
+ async deleteActivity(activity) {
792
+ if (!activity.activityId)
781
793
  throw new Error("Missing activityId");
782
- await this.client.delete(`${this.url.ACTIVITY}${activity2.activityId}`);
794
+ await this.client.delete(`${this.url.ACTIVITY}${activity.activityId}`);
783
795
  }
784
796
  async getWorkouts(start, limit) {
785
797
  return this.client.get(this.url.WORKOUTS, {
786
798
  params: { start, limit }
787
799
  });
788
800
  }
789
- async getWorkoutDetail(workout2) {
790
- if (!workout2.workoutId)
801
+ async getWorkoutDetail(workout) {
802
+ if (!workout.workoutId)
791
803
  throw new Error("Missing workoutId");
792
- return this.client.get(this.url.WORKOUT(workout2.workoutId));
804
+ return this.client.get(this.url.WORKOUT(workout.workoutId));
793
805
  }
794
- async createWorkout(workout2) {
795
- return this.client.post(this.url.WORKOUT(), workout2);
806
+ async createWorkout(workout) {
807
+ return this.client.post(this.url.WORKOUT(), workout);
796
808
  }
797
- async deleteWorkout(workout2) {
798
- if (!workout2.workoutId)
809
+ async deleteWorkout(workout) {
810
+ if (!workout.workoutId)
799
811
  throw new Error("Missing workout");
800
- return this.client.delete(this.url.WORKOUT(workout2.workoutId));
812
+ return this.client.delete(this.url.WORKOUT(workout.workoutId));
801
813
  }
802
- async scheduleWorkout(workout2, scheduleDate) {
803
- return this.client.post(this.url.SCHEDULE_WORKOUT(Number.parseInt(workout2.workoutId)), { date: scheduleDate });
814
+ async scheduleWorkout(workout, scheduleDate) {
815
+ return this.client.post(this.url.SCHEDULE_WORKOUT(Number.parseInt(workout.workoutId)), { date: scheduleDate });
804
816
  }
805
817
  async getSteps(date = new Date) {
806
818
  const dateString = toDateString(date);
@@ -1482,30 +1494,30 @@ class WorkoutType {
1482
1494
  static Cardio = new WorkoutType("cardio_training", 6);
1483
1495
  }
1484
1496
  export {
1485
- courseRequestTemplate,
1486
- WorkoutType,
1487
- WorkoutBuilder,
1488
- UploadFileType,
1489
- TimeDuration,
1490
- Target,
1491
- StepType,
1492
- Step,
1493
- PowerZoneTarget,
1494
- PowerZone,
1495
- PaceTarget,
1496
- NoTarget,
1497
- NoDuration,
1498
- LapPressDuration,
1499
- HrmZoneTarget,
1500
- HrmTarget,
1501
- HeartRateDuration,
1502
- GpxActivityType,
1503
- GarminConnect,
1504
- ExportFileType,
1505
- Duration,
1506
- DistanceDuration,
1507
- CaloriesDuration,
1508
- CadenceTarget,
1497
+ ActivitySubType,
1509
1498
  ActivityType,
1510
- ActivitySubType
1499
+ CadenceTarget,
1500
+ CaloriesDuration,
1501
+ DistanceDuration,
1502
+ Duration,
1503
+ ExportFileType,
1504
+ GarminConnect,
1505
+ GpxActivityType,
1506
+ HeartRateDuration,
1507
+ HrmTarget,
1508
+ HrmZoneTarget,
1509
+ LapPressDuration,
1510
+ NoDuration,
1511
+ NoTarget,
1512
+ PaceTarget,
1513
+ PowerZone,
1514
+ PowerZoneTarget,
1515
+ Step,
1516
+ StepType,
1517
+ Target,
1518
+ TimeDuration,
1519
+ UploadFileType,
1520
+ WorkoutBuilder,
1521
+ WorkoutType,
1522
+ courseRequestTemplate
1511
1523
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ts-garmin",
3
3
  "type": "module",
4
- "version": "0.1.1",
4
+ "version": "0.1.3",
5
5
  "description": "TypeScript library for interfacing with Garmin Connect API",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",