suunto-api-wrapper 1.3.0 → 1.4.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.d.mts CHANGED
@@ -81,6 +81,19 @@ interface LoginResponse {
81
81
  [key: string]: unknown;
82
82
  }
83
83
 
84
+ interface AuthSessionInit {
85
+ email?: string;
86
+ sessionKey?: string;
87
+ }
88
+ declare class AuthSession {
89
+ private readonly XTOTP_HEADER;
90
+ private readonly AUTH_HEADER;
91
+ readonly email?: string;
92
+ readonly sessionKey?: string;
93
+ constructor(init?: AuthSessionInit);
94
+ applyTo(ctx: Pick<RequestContext, "headers">): Promise<void>;
95
+ }
96
+
84
97
  declare const SPORTS_TRACKER_API = "https://api.sports-tracker.com";
85
98
  declare const DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36";
86
99
  declare function login(options: LoginOptions): Promise<LoginResponse>;
@@ -646,18 +659,6 @@ interface WorkoutStatsResponse {
646
659
  };
647
660
  }
648
661
 
649
- declare function getWorkouts(client: HttpClient, username: string, params?: GetWorkoutsParams): Promise<WorkoutsResponse>;
650
- declare function getOwnWorkouts(client: HttpClient, params?: GetOwnWorkoutsParams): Promise<WorkoutsResponse>;
651
- /**
652
- * Public workouts whose center position falls inside the given geographic
653
- * bounding box. Used by the "explore nearby" map view. Unauthenticated.
654
- */
655
- declare function getWorkoutsWithin(client: HttpClient, params: GetWorkoutsWithinParams): Promise<WorkoutsWithinResponse>;
656
- /**
657
- * Aggregated workout stats per activity for a user. Unauthenticated — no
658
- * session required.
659
- */
660
- declare function getWorkoutStats(client: HttpClient, username: string): Promise<WorkoutStatsResponse>;
661
662
  /** Workout endpoints, bound to an {@link HttpClient}. Accessed via `suunto.workouts`. */
662
663
  declare class WorkoutsResource {
663
664
  private readonly client;
@@ -676,17 +677,13 @@ declare class WorkoutsResource {
676
677
  * unauthenticated.
677
678
  */
678
679
  stats(username: string): Promise<WorkoutStatsResponse>;
679
- /** Public workouts whose center falls inside a geographic bounding box. */
680
+ /**
681
+ * Public workouts whose center position falls inside the given geographic
682
+ * bounding box. Used by the "explore nearby" map view. Unauthenticated.
683
+ */
680
684
  within(params: GetWorkoutsWithinParams): Promise<WorkoutsWithinResponse>;
681
685
  }
682
686
 
683
- /**
684
- * Fetch a user's public profile by username. Unauthenticated — no session
685
- * required.
686
- */
687
- declare function getUserByName(client: HttpClient, username: string): Promise<UserProfileResponse>;
688
- /** Search for users by name/username. */
689
- declare function searchUsers(client: HttpClient, searchTerms: string): Promise<UserSearchResponse>;
690
687
  /** User endpoints, bound to an {@link HttpClient}. Accessed via `suunto.users`. */
691
688
  declare class UsersResource {
692
689
  private readonly client;
@@ -714,13 +711,11 @@ interface GearResponse {
714
711
  };
715
712
  }
716
713
 
717
- /** A user's latest gear. */
718
- declare function getLatestGear(client: HttpClient, username: string, params?: GetLatestGearParams): Promise<GearResponse>;
719
714
  /** Gear endpoints, bound to an {@link HttpClient}. Accessed via `suunto.gear`. */
720
715
  declare class GearResource {
721
716
  private readonly client;
722
717
  constructor(client: HttpClient);
723
- /** A user's latest (?) gear. */
718
+ /** A user's latest gear. */
724
719
  latest(username: string, params?: GetLatestGearParams): Promise<GearResponse>;
725
720
  }
726
721
 
@@ -790,25 +785,26 @@ type SleepStagesExportResponse = SleepStageInterval[];
790
785
  type RecoveryExportResponse = RecoveryEntry[];
791
786
  type ActivityExportResponse = ActivityEntry[];
792
787
 
793
- /** Sleep summaries from the 247 service. */
794
- declare function getSleepExport(client: HttpClient, params?: ExportParams): Promise<SleepExportResponse>;
795
- /** Per-stage sleep intervals from the 247 service. */
796
- declare function getSleepStagesExport(client: HttpClient, params?: ExportParams): Promise<SleepStagesExportResponse>;
797
- /** Recovery entries (balance + stress state) from the 247 service. */
798
- declare function getRecoveryExport(client: HttpClient, params?: ExportParams): Promise<RecoveryExportResponse>;
799
- /** Daily activity entries from the 247 service. */
800
- declare function getActivityExport(client: HttpClient, params?: ExportParams): Promise<ActivityExportResponse>;
788
+ interface WellnessResourceOptions extends Omit<HttpClientOptions, "beforeRequest" | "baseUrl"> {
789
+ auth: AuthSession;
790
+ baseUrl?: string;
791
+ }
801
792
  /**
802
793
  * Endpoints served by `https://247.sports-tracker.com` (sleep, recovery,
803
- * activity). Bound to a dedicated {@link HttpClient} configured against that
804
- * host. Accessed via `suunto.wellness`.
794
+ * activity). Owns its own {@link HttpClient} configured against that host,
795
+ * including the wellness-specific wildcard accept header. Accessed via
796
+ * `suunto.wellness`.
805
797
  */
806
798
  declare class WellnessResource {
807
- private readonly client;
808
- constructor(client: HttpClient);
799
+ readonly http: HttpClient;
800
+ constructor(options: WellnessResourceOptions);
801
+ /** Sleep summaries from the 247 service. */
809
802
  sleep(params?: ExportParams): Promise<SleepExportResponse>;
803
+ /** Per-stage sleep intervals from the 247 service. */
810
804
  sleepStages(params?: ExportParams): Promise<SleepStagesExportResponse>;
805
+ /** Recovery entries (balance + stress state) from the 247 service. */
811
806
  recovery(params?: ExportParams): Promise<RecoveryExportResponse>;
807
+ /** Daily activity entries from the 247 service. */
812
808
  activity(params?: ExportParams): Promise<ActivityExportResponse>;
813
809
  }
814
810
 
@@ -840,8 +836,6 @@ declare class SuuntoClient {
840
836
  readonly http: HttpClient;
841
837
  /** Session key in use, if the client was authenticated. */
842
838
  readonly sessionKey?: string;
843
- /** Dedicated HTTP client for the 247 host (sleep/recovery/activity). */
844
- readonly http247: HttpClient;
845
839
  readonly workouts: WorkoutsResource;
846
840
  readonly users: UsersResource;
847
841
  readonly gear: GearResource;
@@ -856,4 +850,4 @@ declare class SuuntoClient {
856
850
  static unauthenticated(options?: Omit<SuuntoClientOptions, "email" | "sessionKey">): SuuntoClient;
857
851
  }
858
852
 
859
- export { type ActivityCounts, type ActivityData, type ActivityEntry, type ActivityExportResponse, type Cadence, type ClientCalculatedAchievements, type CumulativeAchievement, DEFAULT_USER_AGENT, type ExportEntry, type ExportParams, type FitnessExtension, type Gear, GearResource, type GearResponse, type GearSummary, type GetLatestGearParams, type GetOwnWorkoutsParams, type GetWorkoutsParams, type GetWorkoutsWithinParams, type HeartRateRecovery, type HrData, HttpClient, type HttpClientOptions, HttpError, type HttpResponse, type IntensityExtension, type IntensityZone, type IntensityZones, type LoginOptions, type LoginResponse, type PersonalBestAchievement, type Position, type Query, type Rankings, type RecoveryData, type RecoveryEntry, type RecoveryExportResponse, type RequestBody, type RequestContext, type RequestOptions, type RouteRanking, SPORTS_TRACKER_247_API, SPORTS_TRACKER_API, type SearchUser, type SleepExportResponse, type SleepStage, type SleepStageData, type SleepStageInterval, type SleepStagesExportResponse, type SleepSummary, type SleepSummaryData, type SummaryExtension, SuuntoActivityType, SuuntoClient, type SuuntoClientOptions, type SwimmingHeaderExtension, type TssEntry, type UserProfile, type UserProfileResponse, type UserSearchResponse, type UserSearchResult, UsersResource, type WeatherExtension, WellnessResource, type Workout, type WorkoutComment, type WorkoutExtension, type WorkoutReaction, type WorkoutStats, type WorkoutStatsEntry, type WorkoutStatsResponse, WorkoutsResource, type WorkoutsResponse, type WorkoutsWithinItem, type WorkoutsWithinResponse, generateXtotp, getActivityExport, getLatestGear, getOwnWorkouts, getRecoveryExport, getSleepExport, getSleepStagesExport, getUserByName, getWorkoutStats, getWorkouts, getWorkoutsWithin, login, searchUsers, secondsUntilRollover, sessionTokenFrom };
853
+ export { type ActivityCounts, type ActivityData, type ActivityEntry, type ActivityExportResponse, type Cadence, type ClientCalculatedAchievements, type CumulativeAchievement, DEFAULT_USER_AGENT, type ExportEntry, type ExportParams, type FitnessExtension, type Gear, GearResource, type GearResponse, type GearSummary, type GetLatestGearParams, type GetOwnWorkoutsParams, type GetWorkoutsParams, type GetWorkoutsWithinParams, type HeartRateRecovery, type HrData, HttpClient, type HttpClientOptions, HttpError, type HttpResponse, type IntensityExtension, type IntensityZone, type IntensityZones, type LoginOptions, type LoginResponse, type PersonalBestAchievement, type Position, type Query, type Rankings, type RecoveryData, type RecoveryEntry, type RecoveryExportResponse, type RequestBody, type RequestContext, type RequestOptions, type RouteRanking, SPORTS_TRACKER_247_API, SPORTS_TRACKER_API, type SearchUser, type SleepExportResponse, type SleepStage, type SleepStageData, type SleepStageInterval, type SleepStagesExportResponse, type SleepSummary, type SleepSummaryData, type SummaryExtension, SuuntoActivityType, SuuntoClient, type SuuntoClientOptions, type SwimmingHeaderExtension, type TssEntry, type UserProfile, type UserProfileResponse, type UserSearchResponse, type UserSearchResult, UsersResource, type WeatherExtension, WellnessResource, type Workout, type WorkoutComment, type WorkoutExtension, type WorkoutReaction, type WorkoutStats, type WorkoutStatsEntry, type WorkoutStatsResponse, WorkoutsResource, type WorkoutsResponse, type WorkoutsWithinItem, type WorkoutsWithinResponse, generateXtotp, login, secondsUntilRollover, sessionTokenFrom };
package/dist/index.d.ts CHANGED
@@ -81,6 +81,19 @@ interface LoginResponse {
81
81
  [key: string]: unknown;
82
82
  }
83
83
 
84
+ interface AuthSessionInit {
85
+ email?: string;
86
+ sessionKey?: string;
87
+ }
88
+ declare class AuthSession {
89
+ private readonly XTOTP_HEADER;
90
+ private readonly AUTH_HEADER;
91
+ readonly email?: string;
92
+ readonly sessionKey?: string;
93
+ constructor(init?: AuthSessionInit);
94
+ applyTo(ctx: Pick<RequestContext, "headers">): Promise<void>;
95
+ }
96
+
84
97
  declare const SPORTS_TRACKER_API = "https://api.sports-tracker.com";
85
98
  declare const DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36";
86
99
  declare function login(options: LoginOptions): Promise<LoginResponse>;
@@ -646,18 +659,6 @@ interface WorkoutStatsResponse {
646
659
  };
647
660
  }
648
661
 
649
- declare function getWorkouts(client: HttpClient, username: string, params?: GetWorkoutsParams): Promise<WorkoutsResponse>;
650
- declare function getOwnWorkouts(client: HttpClient, params?: GetOwnWorkoutsParams): Promise<WorkoutsResponse>;
651
- /**
652
- * Public workouts whose center position falls inside the given geographic
653
- * bounding box. Used by the "explore nearby" map view. Unauthenticated.
654
- */
655
- declare function getWorkoutsWithin(client: HttpClient, params: GetWorkoutsWithinParams): Promise<WorkoutsWithinResponse>;
656
- /**
657
- * Aggregated workout stats per activity for a user. Unauthenticated — no
658
- * session required.
659
- */
660
- declare function getWorkoutStats(client: HttpClient, username: string): Promise<WorkoutStatsResponse>;
661
662
  /** Workout endpoints, bound to an {@link HttpClient}. Accessed via `suunto.workouts`. */
662
663
  declare class WorkoutsResource {
663
664
  private readonly client;
@@ -676,17 +677,13 @@ declare class WorkoutsResource {
676
677
  * unauthenticated.
677
678
  */
678
679
  stats(username: string): Promise<WorkoutStatsResponse>;
679
- /** Public workouts whose center falls inside a geographic bounding box. */
680
+ /**
681
+ * Public workouts whose center position falls inside the given geographic
682
+ * bounding box. Used by the "explore nearby" map view. Unauthenticated.
683
+ */
680
684
  within(params: GetWorkoutsWithinParams): Promise<WorkoutsWithinResponse>;
681
685
  }
682
686
 
683
- /**
684
- * Fetch a user's public profile by username. Unauthenticated — no session
685
- * required.
686
- */
687
- declare function getUserByName(client: HttpClient, username: string): Promise<UserProfileResponse>;
688
- /** Search for users by name/username. */
689
- declare function searchUsers(client: HttpClient, searchTerms: string): Promise<UserSearchResponse>;
690
687
  /** User endpoints, bound to an {@link HttpClient}. Accessed via `suunto.users`. */
691
688
  declare class UsersResource {
692
689
  private readonly client;
@@ -714,13 +711,11 @@ interface GearResponse {
714
711
  };
715
712
  }
716
713
 
717
- /** A user's latest gear. */
718
- declare function getLatestGear(client: HttpClient, username: string, params?: GetLatestGearParams): Promise<GearResponse>;
719
714
  /** Gear endpoints, bound to an {@link HttpClient}. Accessed via `suunto.gear`. */
720
715
  declare class GearResource {
721
716
  private readonly client;
722
717
  constructor(client: HttpClient);
723
- /** A user's latest (?) gear. */
718
+ /** A user's latest gear. */
724
719
  latest(username: string, params?: GetLatestGearParams): Promise<GearResponse>;
725
720
  }
726
721
 
@@ -790,25 +785,26 @@ type SleepStagesExportResponse = SleepStageInterval[];
790
785
  type RecoveryExportResponse = RecoveryEntry[];
791
786
  type ActivityExportResponse = ActivityEntry[];
792
787
 
793
- /** Sleep summaries from the 247 service. */
794
- declare function getSleepExport(client: HttpClient, params?: ExportParams): Promise<SleepExportResponse>;
795
- /** Per-stage sleep intervals from the 247 service. */
796
- declare function getSleepStagesExport(client: HttpClient, params?: ExportParams): Promise<SleepStagesExportResponse>;
797
- /** Recovery entries (balance + stress state) from the 247 service. */
798
- declare function getRecoveryExport(client: HttpClient, params?: ExportParams): Promise<RecoveryExportResponse>;
799
- /** Daily activity entries from the 247 service. */
800
- declare function getActivityExport(client: HttpClient, params?: ExportParams): Promise<ActivityExportResponse>;
788
+ interface WellnessResourceOptions extends Omit<HttpClientOptions, "beforeRequest" | "baseUrl"> {
789
+ auth: AuthSession;
790
+ baseUrl?: string;
791
+ }
801
792
  /**
802
793
  * Endpoints served by `https://247.sports-tracker.com` (sleep, recovery,
803
- * activity). Bound to a dedicated {@link HttpClient} configured against that
804
- * host. Accessed via `suunto.wellness`.
794
+ * activity). Owns its own {@link HttpClient} configured against that host,
795
+ * including the wellness-specific wildcard accept header. Accessed via
796
+ * `suunto.wellness`.
805
797
  */
806
798
  declare class WellnessResource {
807
- private readonly client;
808
- constructor(client: HttpClient);
799
+ readonly http: HttpClient;
800
+ constructor(options: WellnessResourceOptions);
801
+ /** Sleep summaries from the 247 service. */
809
802
  sleep(params?: ExportParams): Promise<SleepExportResponse>;
803
+ /** Per-stage sleep intervals from the 247 service. */
810
804
  sleepStages(params?: ExportParams): Promise<SleepStagesExportResponse>;
805
+ /** Recovery entries (balance + stress state) from the 247 service. */
811
806
  recovery(params?: ExportParams): Promise<RecoveryExportResponse>;
807
+ /** Daily activity entries from the 247 service. */
812
808
  activity(params?: ExportParams): Promise<ActivityExportResponse>;
813
809
  }
814
810
 
@@ -840,8 +836,6 @@ declare class SuuntoClient {
840
836
  readonly http: HttpClient;
841
837
  /** Session key in use, if the client was authenticated. */
842
838
  readonly sessionKey?: string;
843
- /** Dedicated HTTP client for the 247 host (sleep/recovery/activity). */
844
- readonly http247: HttpClient;
845
839
  readonly workouts: WorkoutsResource;
846
840
  readonly users: UsersResource;
847
841
  readonly gear: GearResource;
@@ -856,4 +850,4 @@ declare class SuuntoClient {
856
850
  static unauthenticated(options?: Omit<SuuntoClientOptions, "email" | "sessionKey">): SuuntoClient;
857
851
  }
858
852
 
859
- export { type ActivityCounts, type ActivityData, type ActivityEntry, type ActivityExportResponse, type Cadence, type ClientCalculatedAchievements, type CumulativeAchievement, DEFAULT_USER_AGENT, type ExportEntry, type ExportParams, type FitnessExtension, type Gear, GearResource, type GearResponse, type GearSummary, type GetLatestGearParams, type GetOwnWorkoutsParams, type GetWorkoutsParams, type GetWorkoutsWithinParams, type HeartRateRecovery, type HrData, HttpClient, type HttpClientOptions, HttpError, type HttpResponse, type IntensityExtension, type IntensityZone, type IntensityZones, type LoginOptions, type LoginResponse, type PersonalBestAchievement, type Position, type Query, type Rankings, type RecoveryData, type RecoveryEntry, type RecoveryExportResponse, type RequestBody, type RequestContext, type RequestOptions, type RouteRanking, SPORTS_TRACKER_247_API, SPORTS_TRACKER_API, type SearchUser, type SleepExportResponse, type SleepStage, type SleepStageData, type SleepStageInterval, type SleepStagesExportResponse, type SleepSummary, type SleepSummaryData, type SummaryExtension, SuuntoActivityType, SuuntoClient, type SuuntoClientOptions, type SwimmingHeaderExtension, type TssEntry, type UserProfile, type UserProfileResponse, type UserSearchResponse, type UserSearchResult, UsersResource, type WeatherExtension, WellnessResource, type Workout, type WorkoutComment, type WorkoutExtension, type WorkoutReaction, type WorkoutStats, type WorkoutStatsEntry, type WorkoutStatsResponse, WorkoutsResource, type WorkoutsResponse, type WorkoutsWithinItem, type WorkoutsWithinResponse, generateXtotp, getActivityExport, getLatestGear, getOwnWorkouts, getRecoveryExport, getSleepExport, getSleepStagesExport, getUserByName, getWorkoutStats, getWorkouts, getWorkoutsWithin, login, searchUsers, secondsUntilRollover, sessionTokenFrom };
853
+ export { type ActivityCounts, type ActivityData, type ActivityEntry, type ActivityExportResponse, type Cadence, type ClientCalculatedAchievements, type CumulativeAchievement, DEFAULT_USER_AGENT, type ExportEntry, type ExportParams, type FitnessExtension, type Gear, GearResource, type GearResponse, type GearSummary, type GetLatestGearParams, type GetOwnWorkoutsParams, type GetWorkoutsParams, type GetWorkoutsWithinParams, type HeartRateRecovery, type HrData, HttpClient, type HttpClientOptions, HttpError, type HttpResponse, type IntensityExtension, type IntensityZone, type IntensityZones, type LoginOptions, type LoginResponse, type PersonalBestAchievement, type Position, type Query, type Rankings, type RecoveryData, type RecoveryEntry, type RecoveryExportResponse, type RequestBody, type RequestContext, type RequestOptions, type RouteRanking, SPORTS_TRACKER_247_API, SPORTS_TRACKER_API, type SearchUser, type SleepExportResponse, type SleepStage, type SleepStageData, type SleepStageInterval, type SleepStagesExportResponse, type SleepSummary, type SleepSummaryData, type SummaryExtension, SuuntoActivityType, SuuntoClient, type SuuntoClientOptions, type SwimmingHeaderExtension, type TssEntry, type UserProfile, type UserProfileResponse, type UserSearchResponse, type UserSearchResult, UsersResource, type WeatherExtension, WellnessResource, type Workout, type WorkoutComment, type WorkoutExtension, type WorkoutReaction, type WorkoutStats, type WorkoutStatsEntry, type WorkoutStatsResponse, WorkoutsResource, type WorkoutsResponse, type WorkoutsWithinItem, type WorkoutsWithinResponse, generateXtotp, login, secondsUntilRollover, sessionTokenFrom };
package/dist/index.js CHANGED
@@ -32,18 +32,7 @@ __export(index_exports, {
32
32
  WellnessResource: () => WellnessResource,
33
33
  WorkoutsResource: () => WorkoutsResource,
34
34
  generateXtotp: () => generateXtotp,
35
- getActivityExport: () => getActivityExport,
36
- getLatestGear: () => getLatestGear,
37
- getOwnWorkouts: () => getOwnWorkouts,
38
- getRecoveryExport: () => getRecoveryExport,
39
- getSleepExport: () => getSleepExport,
40
- getSleepStagesExport: () => getSleepStagesExport,
41
- getUserByName: () => getUserByName,
42
- getWorkoutStats: () => getWorkoutStats,
43
- getWorkouts: () => getWorkouts,
44
- getWorkoutsWithin: () => getWorkoutsWithin,
45
35
  login: () => login,
46
- searchUsers: () => searchUsers,
47
36
  secondsUntilRollover: () => secondsUntilRollover,
48
37
  sessionTokenFrom: () => sessionTokenFrom
49
38
  });
@@ -61,6 +50,12 @@ var HttpError = class extends Error {
61
50
  }
62
51
  };
63
52
 
53
+ // src/http/endpoint.ts
54
+ async function endpoint(client, spec) {
55
+ const res = spec.query === void 0 ? await client.get(spec.path) : await client.get(spec.path, { query: spec.query });
56
+ return res.data;
57
+ }
58
+
64
59
  // src/http/index.ts
65
60
  var DEFAULTS = {
66
61
  timeoutMs: 3e4,
@@ -273,6 +268,20 @@ function secondsUntilRollover(now = Date.now()) {
273
268
  return periodSeconds - Math.floor(now / 1e3) % periodSeconds;
274
269
  }
275
270
 
271
+ // src/auth/session.ts
272
+ var AuthSession = class {
273
+ constructor(init = {}) {
274
+ this.XTOTP_HEADER = "x-totp";
275
+ this.AUTH_HEADER = "sttauthorization";
276
+ this.email = init.email;
277
+ this.sessionKey = init.sessionKey;
278
+ }
279
+ async applyTo(ctx) {
280
+ if (this.email) ctx.headers[this.XTOTP_HEADER] = await generateXtotp(this.email);
281
+ if (this.sessionKey) ctx.headers[this.AUTH_HEADER] = this.sessionKey;
282
+ }
283
+ };
284
+
276
285
  // src/auth/index.ts
277
286
  var SPORTS_TRACKER_API = "https://api.sports-tracker.com";
278
287
  var DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36";
@@ -288,13 +297,14 @@ async function login(options) {
288
297
  } = options;
289
298
  const http = new HttpClient({ baseUrl, fetch: fetchImpl, timeoutMs });
290
299
  const body = new URLSearchParams({ l: email, p: password, version });
300
+ const headers = {
301
+ "user-agent": userAgent,
302
+ "content-type": "application/x-www-form-urlencoded"
303
+ };
304
+ await new AuthSession({ email }).applyTo({ headers });
291
305
  const response = await http.post("/apiserver/v1/login2", {
292
306
  body,
293
- headers: {
294
- "user-agent": userAgent,
295
- "x-totp": await generateXtotp(email),
296
- "content-type": "application/x-www-form-urlencoded"
297
- }
307
+ headers
298
308
  });
299
309
  return response.data;
300
310
  }
@@ -439,138 +449,100 @@ var DEFAULT_WORKOUT_ADDITIONAL_DATA = [
439
449
  "comments" /* Comments */,
440
450
  "user_reacted" /* UserReacted */
441
451
  ];
442
- async function getWorkouts(client, username, params = {}) {
443
- const { limit = 40, sortonst = true } = params;
444
- const res = await client.get(
445
- `/apiserver/v1/workouts/${encodeURIComponent(username)}/public`,
446
- { query: { limit, sortonst } }
447
- );
448
- return res.data;
449
- }
450
- async function getOwnWorkouts(client, params = {}) {
451
- const { offset = 0, limit = 50, since = 0 } = params;
452
- const searchParams = new URLSearchParams();
453
- searchParams.append("offset", String(offset));
454
- searchParams.append("limit", String(limit));
455
- searchParams.append("since", String(since));
456
- const res = await client.get(
457
- `/apiserver/v1/workouts?${searchParams.toString()}`
458
- );
459
- return res.data;
460
- }
461
- async function getWorkout(client, username, workoutKey, params = {}) {
462
- const {
463
- extensions = DEFAULT_WORKOUT_EXTENSIONS,
464
- additionalData = DEFAULT_WORKOUT_ADDITIONAL_DATA
465
- } = params;
466
- const res = await client.get(
467
- `/apiserver/v2/workouts/${encodeURIComponent(username)}/${encodeURIComponent(workoutKey)}/combined`,
468
- {
469
- query: {
470
- extensions: extensions.join(","),
471
- additionalData: additionalData.join(",")
472
- }
473
- }
474
- );
475
- return res.data;
476
- }
477
- async function getWorkoutsWithin(client, params) {
478
- const { lowerLat, lowerLng, upperLat, upperLng, limit = 50 } = params;
479
- const res = await client.get(
480
- "/apiserver/v1/workouts/public/within",
481
- {
482
- query: {
483
- lowerlat: lowerLat,
484
- lowerlng: lowerLng,
485
- upperlat: upperLat,
486
- upperlng: upperLng,
487
- limit
488
- }
489
- }
490
- );
491
- return res.data;
492
- }
493
- async function getWorkoutStats(client, username) {
494
- const res = await client.get(
495
- `/apiserver/v1/workouts/${encodeURIComponent(username)}/stats`
496
- );
497
- return res.data;
498
- }
499
452
  var WorkoutsResource = class {
500
453
  constructor(client) {
501
454
  this.client = client;
502
455
  }
503
456
  /** The authenticated user's own workouts. */
504
- own(params) {
505
- return getOwnWorkouts(this.client, params);
457
+ own(params = {}) {
458
+ return endpoint(this.client, {
459
+ path: "/apiserver/v1/workouts",
460
+ query: {
461
+ offset: params.offset ?? 0,
462
+ limit: params.limit ?? 50,
463
+ since: params.since ?? 0
464
+ }
465
+ });
506
466
  }
507
467
  /** A given user's public workouts. */
508
- public(username, params) {
509
- return getWorkouts(this.client, username, params);
468
+ public(username, params = {}) {
469
+ return endpoint(this.client, {
470
+ path: `/apiserver/v1/workouts/${encodeURIComponent(username)}/public`,
471
+ query: { limit: params.limit ?? 40, sortonst: params.sortonst ?? true }
472
+ });
510
473
  }
511
474
  /**
512
475
  * A single workout by username and workout key. Works for any public workout
513
476
  * and, when the client is authenticated as the owner, for private ones too.
514
477
  */
515
- byKey(username, workoutKey, params) {
516
- return getWorkout(this.client, username, workoutKey, params);
478
+ byKey(username, workoutKey, params = {}) {
479
+ const extensions = params.extensions ?? DEFAULT_WORKOUT_EXTENSIONS;
480
+ const additionalData = params.additionalData ?? DEFAULT_WORKOUT_ADDITIONAL_DATA;
481
+ return endpoint(this.client, {
482
+ path: `/apiserver/v2/workouts/${encodeURIComponent(username)}/${encodeURIComponent(workoutKey)}/combined`,
483
+ query: {
484
+ extensions: extensions.join(","),
485
+ additionalData: additionalData.join(",")
486
+ }
487
+ });
517
488
  }
518
489
  /**
519
490
  * Aggregated workout stats per activity for the given user. Works
520
491
  * unauthenticated.
521
492
  */
522
493
  stats(username) {
523
- return getWorkoutStats(this.client, username);
494
+ return endpoint(this.client, {
495
+ path: `/apiserver/v1/workouts/${encodeURIComponent(username)}/stats`
496
+ });
524
497
  }
525
- /** Public workouts whose center falls inside a geographic bounding box. */
498
+ /**
499
+ * Public workouts whose center position falls inside the given geographic
500
+ * bounding box. Used by the "explore nearby" map view. Unauthenticated.
501
+ */
526
502
  within(params) {
527
- return getWorkoutsWithin(this.client, params);
503
+ return endpoint(this.client, {
504
+ path: "/apiserver/v1/workouts/public/within",
505
+ query: {
506
+ lowerlat: params.lowerLat,
507
+ lowerlng: params.lowerLng,
508
+ upperlat: params.upperLat,
509
+ upperlng: params.upperLng,
510
+ limit: params.limit ?? 50
511
+ }
512
+ });
528
513
  }
529
514
  };
530
515
 
531
516
  // src/users/index.ts
532
- async function getUserByName(client, username) {
533
- const res = await client.get(
534
- `/apiserver/v1/user/name/${encodeURIComponent(username)}`
535
- );
536
- return res.data;
537
- }
538
- async function searchUsers(client, searchTerms) {
539
- const res = await client.get(
540
- `/apiserver/v1/user/search/${encodeURIComponent(searchTerms)}`
541
- );
542
- return res.data;
543
- }
544
517
  var UsersResource = class {
545
518
  constructor(client) {
546
519
  this.client = client;
547
520
  }
548
521
  /** A user's public profile, by username. */
549
522
  byName(username) {
550
- return getUserByName(this.client, username);
523
+ return endpoint(this.client, {
524
+ path: `/apiserver/v1/user/name/${encodeURIComponent(username)}`
525
+ });
551
526
  }
552
527
  /** Search for users by name/username. */
553
528
  search(searchTerms) {
554
- return searchUsers(this.client, searchTerms);
529
+ return endpoint(this.client, {
530
+ path: `/apiserver/v1/user/search/${encodeURIComponent(searchTerms)}`
531
+ });
555
532
  }
556
533
  };
557
534
 
558
535
  // src/gear/index.ts
559
- async function getLatestGear(client, username, params = {}) {
560
- const { allTypes = true } = params;
561
- const res = await client.get(
562
- `/apiserver/v1/gear/${encodeURIComponent(username)}/latest`,
563
- { query: { allTypes } }
564
- );
565
- return res.data;
566
- }
567
536
  var GearResource = class {
568
537
  constructor(client) {
569
538
  this.client = client;
570
539
  }
571
- /** A user's latest (?) gear. */
572
- latest(username, params) {
573
- return getLatestGear(this.client, username, params);
540
+ /** A user's latest gear. */
541
+ latest(username, params = {}) {
542
+ return endpoint(this.client, {
543
+ path: `/apiserver/v1/gear/${encodeURIComponent(username)}/latest`,
544
+ query: { allTypes: params.allTypes ?? true }
545
+ });
574
546
  }
575
547
  };
576
548
 
@@ -578,49 +550,46 @@ var GearResource = class {
578
550
  var SPORTS_TRACKER_247_API = "https://247.sports-tracker.com";
579
551
 
580
552
  // src/wellness/index.ts
581
- function exportQuery(params) {
582
- return params.since == null ? void 0 : { since: params.since };
583
- }
584
- async function getSleepExport(client, params = {}) {
585
- const res = await client.get("/v1/sleep/export", {
586
- query: exportQuery(params)
587
- });
588
- return res.data;
589
- }
590
- async function getSleepStagesExport(client, params = {}) {
591
- const res = await client.get(
592
- "/v1/sleepstages/export",
593
- { query: exportQuery(params) }
594
- );
595
- return res.data;
596
- }
597
- async function getRecoveryExport(client, params = {}) {
598
- const res = await client.get("/v1/recovery/export", {
599
- query: exportQuery(params)
600
- });
601
- return res.data;
602
- }
603
- async function getActivityExport(client, params = {}) {
604
- const res = await client.get("/v1/activity/export", {
605
- query: exportQuery(params)
606
- });
607
- return res.data;
608
- }
553
+ var exportQuery = (params) => params.since == null ? void 0 : { since: params.since };
609
554
  var WellnessResource = class {
610
- constructor(client) {
611
- this.client = client;
555
+ constructor(options) {
556
+ const { auth, baseUrl, ...rest } = options;
557
+ this.http = new HttpClient({
558
+ ...rest,
559
+ baseUrl: baseUrl ?? SPORTS_TRACKER_247_API,
560
+ beforeRequest: async (ctx) => {
561
+ await auth.applyTo(ctx);
562
+ ctx.headers["accept"] = "*/*";
563
+ }
564
+ });
612
565
  }
613
- sleep(params) {
614
- return getSleepExport(this.client, params);
566
+ /** Sleep summaries from the 247 service. */
567
+ sleep(params = {}) {
568
+ return endpoint(this.http, {
569
+ path: "/v1/sleep/export",
570
+ query: exportQuery(params)
571
+ });
615
572
  }
616
- sleepStages(params) {
617
- return getSleepStagesExport(this.client, params);
573
+ /** Per-stage sleep intervals from the 247 service. */
574
+ sleepStages(params = {}) {
575
+ return endpoint(this.http, {
576
+ path: "/v1/sleepstages/export",
577
+ query: exportQuery(params)
578
+ });
618
579
  }
619
- recovery(params) {
620
- return getRecoveryExport(this.client, params);
580
+ /** Recovery entries (balance + stress state) from the 247 service. */
581
+ recovery(params = {}) {
582
+ return endpoint(this.http, {
583
+ path: "/v1/recovery/export",
584
+ query: exportQuery(params)
585
+ });
621
586
  }
622
- activity(params) {
623
- return getActivityExport(this.client, params);
587
+ /** Daily activity entries from the 247 service. */
588
+ activity(params = {}) {
589
+ return endpoint(this.http, {
590
+ path: "/v1/activity/export",
591
+ query: exportQuery(params)
592
+ });
624
593
  }
625
594
  };
626
595
 
@@ -637,27 +606,23 @@ var SuuntoClient = class _SuuntoClient {
637
606
  ...rest
638
607
  } = options;
639
608
  this.sessionKey = sessionKey;
640
- const beforeRequest = async (ctx) => {
641
- if (email) ctx.headers["x-totp"] = await generateXtotp(email);
642
- if (sessionKey) ctx.headers["sttauthorization"] = sessionKey;
643
- };
609
+ const auth = new AuthSession({ email, sessionKey });
644
610
  const sharedHeaders = { "user-agent": userAgent, ...headers };
645
611
  this.http = new HttpClient({
646
612
  baseUrl: baseUrl ?? SPORTS_TRACKER_API,
647
613
  headers: sharedHeaders,
648
614
  ...rest,
649
- beforeRequest
650
- });
651
- this.http247 = new HttpClient({
652
- baseUrl: baseUrl247 ?? SPORTS_TRACKER_247_API,
653
- headers: sharedHeaders,
654
- ...rest,
655
- beforeRequest
615
+ beforeRequest: (ctx) => auth.applyTo(ctx)
656
616
  });
657
617
  this.workouts = new WorkoutsResource(this.http);
658
618
  this.users = new UsersResource(this.http);
659
619
  this.gear = new GearResource(this.http);
660
- this.wellness = new WellnessResource(this.http247);
620
+ this.wellness = new WellnessResource({
621
+ auth,
622
+ baseUrl: baseUrl247,
623
+ headers: sharedHeaders,
624
+ ...rest
625
+ });
661
626
  }
662
627
  /** Log in with email/password and return an authenticated client. */
663
628
  static async login(options) {
@@ -688,18 +653,7 @@ var SuuntoClient = class _SuuntoClient {
688
653
  WellnessResource,
689
654
  WorkoutsResource,
690
655
  generateXtotp,
691
- getActivityExport,
692
- getLatestGear,
693
- getOwnWorkouts,
694
- getRecoveryExport,
695
- getSleepExport,
696
- getSleepStagesExport,
697
- getUserByName,
698
- getWorkoutStats,
699
- getWorkouts,
700
- getWorkoutsWithin,
701
656
  login,
702
- searchUsers,
703
657
  secondsUntilRollover,
704
658
  sessionTokenFrom
705
659
  });
package/dist/index.mjs CHANGED
@@ -10,6 +10,12 @@ var HttpError = class extends Error {
10
10
  }
11
11
  };
12
12
 
13
+ // src/http/endpoint.ts
14
+ async function endpoint(client, spec) {
15
+ const res = spec.query === void 0 ? await client.get(spec.path) : await client.get(spec.path, { query: spec.query });
16
+ return res.data;
17
+ }
18
+
13
19
  // src/http/index.ts
14
20
  var DEFAULTS = {
15
21
  timeoutMs: 3e4,
@@ -222,6 +228,20 @@ function secondsUntilRollover(now = Date.now()) {
222
228
  return periodSeconds - Math.floor(now / 1e3) % periodSeconds;
223
229
  }
224
230
 
231
+ // src/auth/session.ts
232
+ var AuthSession = class {
233
+ constructor(init = {}) {
234
+ this.XTOTP_HEADER = "x-totp";
235
+ this.AUTH_HEADER = "sttauthorization";
236
+ this.email = init.email;
237
+ this.sessionKey = init.sessionKey;
238
+ }
239
+ async applyTo(ctx) {
240
+ if (this.email) ctx.headers[this.XTOTP_HEADER] = await generateXtotp(this.email);
241
+ if (this.sessionKey) ctx.headers[this.AUTH_HEADER] = this.sessionKey;
242
+ }
243
+ };
244
+
225
245
  // src/auth/index.ts
226
246
  var SPORTS_TRACKER_API = "https://api.sports-tracker.com";
227
247
  var DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36";
@@ -237,13 +257,14 @@ async function login(options) {
237
257
  } = options;
238
258
  const http = new HttpClient({ baseUrl, fetch: fetchImpl, timeoutMs });
239
259
  const body = new URLSearchParams({ l: email, p: password, version });
260
+ const headers = {
261
+ "user-agent": userAgent,
262
+ "content-type": "application/x-www-form-urlencoded"
263
+ };
264
+ await new AuthSession({ email }).applyTo({ headers });
240
265
  const response = await http.post("/apiserver/v1/login2", {
241
266
  body,
242
- headers: {
243
- "user-agent": userAgent,
244
- "x-totp": await generateXtotp(email),
245
- "content-type": "application/x-www-form-urlencoded"
246
- }
267
+ headers
247
268
  });
248
269
  return response.data;
249
270
  }
@@ -388,138 +409,100 @@ var DEFAULT_WORKOUT_ADDITIONAL_DATA = [
388
409
  "comments" /* Comments */,
389
410
  "user_reacted" /* UserReacted */
390
411
  ];
391
- async function getWorkouts(client, username, params = {}) {
392
- const { limit = 40, sortonst = true } = params;
393
- const res = await client.get(
394
- `/apiserver/v1/workouts/${encodeURIComponent(username)}/public`,
395
- { query: { limit, sortonst } }
396
- );
397
- return res.data;
398
- }
399
- async function getOwnWorkouts(client, params = {}) {
400
- const { offset = 0, limit = 50, since = 0 } = params;
401
- const searchParams = new URLSearchParams();
402
- searchParams.append("offset", String(offset));
403
- searchParams.append("limit", String(limit));
404
- searchParams.append("since", String(since));
405
- const res = await client.get(
406
- `/apiserver/v1/workouts?${searchParams.toString()}`
407
- );
408
- return res.data;
409
- }
410
- async function getWorkout(client, username, workoutKey, params = {}) {
411
- const {
412
- extensions = DEFAULT_WORKOUT_EXTENSIONS,
413
- additionalData = DEFAULT_WORKOUT_ADDITIONAL_DATA
414
- } = params;
415
- const res = await client.get(
416
- `/apiserver/v2/workouts/${encodeURIComponent(username)}/${encodeURIComponent(workoutKey)}/combined`,
417
- {
418
- query: {
419
- extensions: extensions.join(","),
420
- additionalData: additionalData.join(",")
421
- }
422
- }
423
- );
424
- return res.data;
425
- }
426
- async function getWorkoutsWithin(client, params) {
427
- const { lowerLat, lowerLng, upperLat, upperLng, limit = 50 } = params;
428
- const res = await client.get(
429
- "/apiserver/v1/workouts/public/within",
430
- {
431
- query: {
432
- lowerlat: lowerLat,
433
- lowerlng: lowerLng,
434
- upperlat: upperLat,
435
- upperlng: upperLng,
436
- limit
437
- }
438
- }
439
- );
440
- return res.data;
441
- }
442
- async function getWorkoutStats(client, username) {
443
- const res = await client.get(
444
- `/apiserver/v1/workouts/${encodeURIComponent(username)}/stats`
445
- );
446
- return res.data;
447
- }
448
412
  var WorkoutsResource = class {
449
413
  constructor(client) {
450
414
  this.client = client;
451
415
  }
452
416
  /** The authenticated user's own workouts. */
453
- own(params) {
454
- return getOwnWorkouts(this.client, params);
417
+ own(params = {}) {
418
+ return endpoint(this.client, {
419
+ path: "/apiserver/v1/workouts",
420
+ query: {
421
+ offset: params.offset ?? 0,
422
+ limit: params.limit ?? 50,
423
+ since: params.since ?? 0
424
+ }
425
+ });
455
426
  }
456
427
  /** A given user's public workouts. */
457
- public(username, params) {
458
- return getWorkouts(this.client, username, params);
428
+ public(username, params = {}) {
429
+ return endpoint(this.client, {
430
+ path: `/apiserver/v1/workouts/${encodeURIComponent(username)}/public`,
431
+ query: { limit: params.limit ?? 40, sortonst: params.sortonst ?? true }
432
+ });
459
433
  }
460
434
  /**
461
435
  * A single workout by username and workout key. Works for any public workout
462
436
  * and, when the client is authenticated as the owner, for private ones too.
463
437
  */
464
- byKey(username, workoutKey, params) {
465
- return getWorkout(this.client, username, workoutKey, params);
438
+ byKey(username, workoutKey, params = {}) {
439
+ const extensions = params.extensions ?? DEFAULT_WORKOUT_EXTENSIONS;
440
+ const additionalData = params.additionalData ?? DEFAULT_WORKOUT_ADDITIONAL_DATA;
441
+ return endpoint(this.client, {
442
+ path: `/apiserver/v2/workouts/${encodeURIComponent(username)}/${encodeURIComponent(workoutKey)}/combined`,
443
+ query: {
444
+ extensions: extensions.join(","),
445
+ additionalData: additionalData.join(",")
446
+ }
447
+ });
466
448
  }
467
449
  /**
468
450
  * Aggregated workout stats per activity for the given user. Works
469
451
  * unauthenticated.
470
452
  */
471
453
  stats(username) {
472
- return getWorkoutStats(this.client, username);
454
+ return endpoint(this.client, {
455
+ path: `/apiserver/v1/workouts/${encodeURIComponent(username)}/stats`
456
+ });
473
457
  }
474
- /** Public workouts whose center falls inside a geographic bounding box. */
458
+ /**
459
+ * Public workouts whose center position falls inside the given geographic
460
+ * bounding box. Used by the "explore nearby" map view. Unauthenticated.
461
+ */
475
462
  within(params) {
476
- return getWorkoutsWithin(this.client, params);
463
+ return endpoint(this.client, {
464
+ path: "/apiserver/v1/workouts/public/within",
465
+ query: {
466
+ lowerlat: params.lowerLat,
467
+ lowerlng: params.lowerLng,
468
+ upperlat: params.upperLat,
469
+ upperlng: params.upperLng,
470
+ limit: params.limit ?? 50
471
+ }
472
+ });
477
473
  }
478
474
  };
479
475
 
480
476
  // src/users/index.ts
481
- async function getUserByName(client, username) {
482
- const res = await client.get(
483
- `/apiserver/v1/user/name/${encodeURIComponent(username)}`
484
- );
485
- return res.data;
486
- }
487
- async function searchUsers(client, searchTerms) {
488
- const res = await client.get(
489
- `/apiserver/v1/user/search/${encodeURIComponent(searchTerms)}`
490
- );
491
- return res.data;
492
- }
493
477
  var UsersResource = class {
494
478
  constructor(client) {
495
479
  this.client = client;
496
480
  }
497
481
  /** A user's public profile, by username. */
498
482
  byName(username) {
499
- return getUserByName(this.client, username);
483
+ return endpoint(this.client, {
484
+ path: `/apiserver/v1/user/name/${encodeURIComponent(username)}`
485
+ });
500
486
  }
501
487
  /** Search for users by name/username. */
502
488
  search(searchTerms) {
503
- return searchUsers(this.client, searchTerms);
489
+ return endpoint(this.client, {
490
+ path: `/apiserver/v1/user/search/${encodeURIComponent(searchTerms)}`
491
+ });
504
492
  }
505
493
  };
506
494
 
507
495
  // src/gear/index.ts
508
- async function getLatestGear(client, username, params = {}) {
509
- const { allTypes = true } = params;
510
- const res = await client.get(
511
- `/apiserver/v1/gear/${encodeURIComponent(username)}/latest`,
512
- { query: { allTypes } }
513
- );
514
- return res.data;
515
- }
516
496
  var GearResource = class {
517
497
  constructor(client) {
518
498
  this.client = client;
519
499
  }
520
- /** A user's latest (?) gear. */
521
- latest(username, params) {
522
- return getLatestGear(this.client, username, params);
500
+ /** A user's latest gear. */
501
+ latest(username, params = {}) {
502
+ return endpoint(this.client, {
503
+ path: `/apiserver/v1/gear/${encodeURIComponent(username)}/latest`,
504
+ query: { allTypes: params.allTypes ?? true }
505
+ });
523
506
  }
524
507
  };
525
508
 
@@ -527,49 +510,46 @@ var GearResource = class {
527
510
  var SPORTS_TRACKER_247_API = "https://247.sports-tracker.com";
528
511
 
529
512
  // src/wellness/index.ts
530
- function exportQuery(params) {
531
- return params.since == null ? void 0 : { since: params.since };
532
- }
533
- async function getSleepExport(client, params = {}) {
534
- const res = await client.get("/v1/sleep/export", {
535
- query: exportQuery(params)
536
- });
537
- return res.data;
538
- }
539
- async function getSleepStagesExport(client, params = {}) {
540
- const res = await client.get(
541
- "/v1/sleepstages/export",
542
- { query: exportQuery(params) }
543
- );
544
- return res.data;
545
- }
546
- async function getRecoveryExport(client, params = {}) {
547
- const res = await client.get("/v1/recovery/export", {
548
- query: exportQuery(params)
549
- });
550
- return res.data;
551
- }
552
- async function getActivityExport(client, params = {}) {
553
- const res = await client.get("/v1/activity/export", {
554
- query: exportQuery(params)
555
- });
556
- return res.data;
557
- }
513
+ var exportQuery = (params) => params.since == null ? void 0 : { since: params.since };
558
514
  var WellnessResource = class {
559
- constructor(client) {
560
- this.client = client;
515
+ constructor(options) {
516
+ const { auth, baseUrl, ...rest } = options;
517
+ this.http = new HttpClient({
518
+ ...rest,
519
+ baseUrl: baseUrl ?? SPORTS_TRACKER_247_API,
520
+ beforeRequest: async (ctx) => {
521
+ await auth.applyTo(ctx);
522
+ ctx.headers["accept"] = "*/*";
523
+ }
524
+ });
561
525
  }
562
- sleep(params) {
563
- return getSleepExport(this.client, params);
526
+ /** Sleep summaries from the 247 service. */
527
+ sleep(params = {}) {
528
+ return endpoint(this.http, {
529
+ path: "/v1/sleep/export",
530
+ query: exportQuery(params)
531
+ });
564
532
  }
565
- sleepStages(params) {
566
- return getSleepStagesExport(this.client, params);
533
+ /** Per-stage sleep intervals from the 247 service. */
534
+ sleepStages(params = {}) {
535
+ return endpoint(this.http, {
536
+ path: "/v1/sleepstages/export",
537
+ query: exportQuery(params)
538
+ });
567
539
  }
568
- recovery(params) {
569
- return getRecoveryExport(this.client, params);
540
+ /** Recovery entries (balance + stress state) from the 247 service. */
541
+ recovery(params = {}) {
542
+ return endpoint(this.http, {
543
+ path: "/v1/recovery/export",
544
+ query: exportQuery(params)
545
+ });
570
546
  }
571
- activity(params) {
572
- return getActivityExport(this.client, params);
547
+ /** Daily activity entries from the 247 service. */
548
+ activity(params = {}) {
549
+ return endpoint(this.http, {
550
+ path: "/v1/activity/export",
551
+ query: exportQuery(params)
552
+ });
573
553
  }
574
554
  };
575
555
 
@@ -586,27 +566,23 @@ var SuuntoClient = class _SuuntoClient {
586
566
  ...rest
587
567
  } = options;
588
568
  this.sessionKey = sessionKey;
589
- const beforeRequest = async (ctx) => {
590
- if (email) ctx.headers["x-totp"] = await generateXtotp(email);
591
- if (sessionKey) ctx.headers["sttauthorization"] = sessionKey;
592
- };
569
+ const auth = new AuthSession({ email, sessionKey });
593
570
  const sharedHeaders = { "user-agent": userAgent, ...headers };
594
571
  this.http = new HttpClient({
595
572
  baseUrl: baseUrl ?? SPORTS_TRACKER_API,
596
573
  headers: sharedHeaders,
597
574
  ...rest,
598
- beforeRequest
599
- });
600
- this.http247 = new HttpClient({
601
- baseUrl: baseUrl247 ?? SPORTS_TRACKER_247_API,
602
- headers: sharedHeaders,
603
- ...rest,
604
- beforeRequest
575
+ beforeRequest: (ctx) => auth.applyTo(ctx)
605
576
  });
606
577
  this.workouts = new WorkoutsResource(this.http);
607
578
  this.users = new UsersResource(this.http);
608
579
  this.gear = new GearResource(this.http);
609
- this.wellness = new WellnessResource(this.http247);
580
+ this.wellness = new WellnessResource({
581
+ auth,
582
+ baseUrl: baseUrl247,
583
+ headers: sharedHeaders,
584
+ ...rest
585
+ });
610
586
  }
611
587
  /** Log in with email/password and return an authenticated client. */
612
588
  static async login(options) {
@@ -636,18 +612,7 @@ export {
636
612
  WellnessResource,
637
613
  WorkoutsResource,
638
614
  generateXtotp,
639
- getActivityExport,
640
- getLatestGear,
641
- getOwnWorkouts,
642
- getRecoveryExport,
643
- getSleepExport,
644
- getSleepStagesExport,
645
- getUserByName,
646
- getWorkoutStats,
647
- getWorkouts,
648
- getWorkoutsWithin,
649
615
  login,
650
- searchUsers,
651
616
  secondsUntilRollover,
652
617
  sessionTokenFrom
653
618
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suunto-api-wrapper",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Unofficial typed TypeScript client for the Suunto app API (Sports Tracker backend). Not affiliated with or endorsed by Suunto or Sports Tracker.",
5
5
  "repository": {
6
6
  "url": "https://github.com/Marius-Ar/suunto-api-wrapper"