@dlwiest/ts-tonal-client 0.3.1 → 0.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/README.md +42 -0
- package/dist/index.cjs +38 -0
- package/dist/index.d.ts +27 -1
- package/dist/index.esm.js +38 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -441,6 +441,48 @@ if (volumeScores && volumeScores.length >= 2) {
|
|
|
441
441
|
}
|
|
442
442
|
```
|
|
443
443
|
|
|
444
|
+
### Strength Scores
|
|
445
|
+
|
|
446
|
+
Tonal's headline Strength Score, per body region. This is a different metric from the weekly
|
|
447
|
+
`Functional Strength Score` returned by `getGoalMetrics()` — that one measures goal progress
|
|
448
|
+
for a week, this one is the score the Tonal app shows you.
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
// Current score for each region
|
|
452
|
+
const scores = await client.getCurrentStrengthScores()
|
|
453
|
+
for (const score of scores) {
|
|
454
|
+
// The Overall row is synthesized: bodyRegionDisplay is empty, familyActivity is absent,
|
|
455
|
+
// workoutActivityId is an all-zero uuid, and updatedAt is a zero date. Fall back to
|
|
456
|
+
// strengthBodyRegion for a label, and do not render its updatedAt.
|
|
457
|
+
const label = score.bodyRegionDisplay || score.strengthBodyRegion
|
|
458
|
+
console.log(`${label}: ${score.score}`)
|
|
459
|
+
}
|
|
460
|
+
// One row per region: Upper Body, Core, Lower Body, Overall
|
|
461
|
+
|
|
462
|
+
// Per-workout history. Defaults to the whole account.
|
|
463
|
+
const history = await client.getStrengthScoreHistory()
|
|
464
|
+
console.log(`${history.length} scored activities`)
|
|
465
|
+
console.log(history[0]) // { upper, lower, core, overall, activityTime, workoutActivityId, ... }
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
**`days` is a calendar-day lookback, not a row count.** The underlying API parameter is named
|
|
469
|
+
`limit`, but it selects a time window: a value smaller than the gap since your last workout
|
|
470
|
+
returns an **empty array**, not "no results found". Passing `30` on an account last used
|
|
471
|
+
90 days ago yields nothing.
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
await client.getStrengthScoreHistory(365) // activities in the last 365 days
|
|
475
|
+
await client.getStrengthScoreHistory('all') // explicit; same as the default
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
The default `'all'` derives the window from your account creation date, so it stays correct as
|
|
479
|
+
the account ages rather than relying on a large magic number. If `createdAt` is missing,
|
|
480
|
+
unparseable, or in the future it throws `TonalClientError` rather than guessing — pass an
|
|
481
|
+
explicit `days` in that case.
|
|
482
|
+
|
|
483
|
+
Each history entry carries `workoutActivityId`, which makes this the only complete way to
|
|
484
|
+
enumerate an account's activities: the list endpoints are capped at 50 records each.
|
|
485
|
+
|
|
444
486
|
### Movements
|
|
445
487
|
|
|
446
488
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -518,6 +518,16 @@ class UserService {
|
|
|
518
518
|
async getCurrentStreak(userId) {
|
|
519
519
|
return this.httpClient.request(`/users/${userId}/streaks/current`);
|
|
520
520
|
}
|
|
521
|
+
async getCurrentStrengthScores(userId) {
|
|
522
|
+
return this.httpClient.request(`/users/${userId}/strength-scores/current`);
|
|
523
|
+
}
|
|
524
|
+
async getStrengthScoreHistory(userId, days) {
|
|
525
|
+
if (!Number.isSafeInteger(days) || days <= 0) {
|
|
526
|
+
throw new TonalClientError('Strength score history days must be a positive safe integer');
|
|
527
|
+
}
|
|
528
|
+
// Tonal's "limit" query parameter is a calendar-day window; small values return an empty array.
|
|
529
|
+
return this.httpClient.request(`/users/${userId}/strength-scores/history?limit=${days}`);
|
|
530
|
+
}
|
|
521
531
|
async getActivitySummaries(userId) {
|
|
522
532
|
return this.httpClient.request(`/users/${userId}/activity-summaries`);
|
|
523
533
|
}
|
|
@@ -623,6 +633,34 @@ class TonalClient {
|
|
|
623
633
|
const userInfo = await this.getUserInfo();
|
|
624
634
|
return this.userService.getCurrentStreak(userInfo.id);
|
|
625
635
|
}
|
|
636
|
+
async getCurrentStrengthScores() {
|
|
637
|
+
const userInfo = await this.getUserInfo();
|
|
638
|
+
return this.userService.getCurrentStrengthScores(userInfo.id);
|
|
639
|
+
}
|
|
640
|
+
async getStrengthScoreHistory(days = 'all') {
|
|
641
|
+
if (days !== 'all' && (!Number.isSafeInteger(days) || days <= 0)) {
|
|
642
|
+
throw new TonalClientError('Strength score history days must be a positive safe integer');
|
|
643
|
+
}
|
|
644
|
+
const userInfo = await this.getUserInfo();
|
|
645
|
+
let lookbackDays;
|
|
646
|
+
if (days === 'all') {
|
|
647
|
+
const createdAt = userInfo.createdAt;
|
|
648
|
+
const createdAtMs = typeof createdAt === 'string' ? Date.parse(createdAt) : Number.NaN;
|
|
649
|
+
const now = Date.now();
|
|
650
|
+
if (!Number.isFinite(createdAtMs) || createdAtMs > now) {
|
|
651
|
+
throw new TonalClientError('Cannot derive all strength score history from user createdAt; pass explicit days');
|
|
652
|
+
}
|
|
653
|
+
// +2 covers the account-creation calendar day and the server's midnight-boundary
|
|
654
|
+
// ambiguity; over-fetching before account creation is harmless. The Math.max floor is
|
|
655
|
+
// belt-and-braces only -- the future-createdAt rejection above already guarantees a
|
|
656
|
+
// non-negative difference, so this expression is always >= 2.
|
|
657
|
+
lookbackDays = Math.max(1, Math.ceil((now - createdAtMs) / 86400000) + 2);
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
lookbackDays = days;
|
|
661
|
+
}
|
|
662
|
+
return this.userService.getStrengthScoreHistory(userInfo.id, lookbackDays);
|
|
663
|
+
}
|
|
626
664
|
async getActivitySummaries() {
|
|
627
665
|
const userInfo = await this.getUserInfo();
|
|
628
666
|
return this.userService.getActivitySummaries(userInfo.id);
|
package/dist/index.d.ts
CHANGED
|
@@ -373,6 +373,30 @@ interface TonalCurrentStreak {
|
|
|
373
373
|
maxStreakStartDate: string;
|
|
374
374
|
updatedByActivityId: string;
|
|
375
375
|
}
|
|
376
|
+
type TonalStrengthBodyRegion = 'Upper Body' | 'Core' | 'Lower Body' | 'Overall' | string;
|
|
377
|
+
interface TonalStrengthScore {
|
|
378
|
+
id: string;
|
|
379
|
+
createdAt: string;
|
|
380
|
+
updatedAt: string;
|
|
381
|
+
userId: string;
|
|
382
|
+
workoutActivityId: string;
|
|
383
|
+
strengthBodyRegion: TonalStrengthBodyRegion;
|
|
384
|
+
bodyRegionDisplay: string;
|
|
385
|
+
score: number;
|
|
386
|
+
current: boolean;
|
|
387
|
+
familyActivity?: unknown[];
|
|
388
|
+
}
|
|
389
|
+
interface TonalStrengthScoreHistoryEntry {
|
|
390
|
+
id: string;
|
|
391
|
+
userId: string;
|
|
392
|
+
workoutActivityId: string;
|
|
393
|
+
upper: number;
|
|
394
|
+
lower: number;
|
|
395
|
+
core: number;
|
|
396
|
+
overall: number;
|
|
397
|
+
activityTime: string;
|
|
398
|
+
}
|
|
399
|
+
type TonalStrengthScoreHistoryLookback = number | 'all';
|
|
376
400
|
interface TonalActivitySummary {
|
|
377
401
|
id: string;
|
|
378
402
|
deletedAt: string | null;
|
|
@@ -661,6 +685,8 @@ declare class TonalClient {
|
|
|
661
685
|
getUserSettings(): Promise<TonalUserSettings>;
|
|
662
686
|
getDailyMetrics(days?: number): Promise<TonalDailyMetrics[]>;
|
|
663
687
|
getCurrentStreak(): Promise<TonalCurrentStreak>;
|
|
688
|
+
getCurrentStrengthScores(): Promise<TonalStrengthScore[]>;
|
|
689
|
+
getStrengthScoreHistory(days?: TonalStrengthScoreHistoryLookback): Promise<TonalStrengthScoreHistoryEntry[]>;
|
|
664
690
|
getActivitySummaries(): Promise<TonalActivitySummary[]>;
|
|
665
691
|
getUserStatistics(): Promise<TonalUserStatistics>;
|
|
666
692
|
getAchievementStats(): Promise<TonalAchievementStats>;
|
|
@@ -680,4 +706,4 @@ declare class TonalClient {
|
|
|
680
706
|
deleteWorkout(workoutId: string): Promise<void>;
|
|
681
707
|
}
|
|
682
708
|
|
|
683
|
-
export { MuscleGroup, OAuthTokenResponse, TonalAchievementCategory, TonalAchievementDefinition, TonalAchievementMilestone, TonalAchievementStats, TonalActivitySummary, TonalApiError, TonalCalendarTile, TonalClient, TonalClientError, TonalCompatibilityStatus, TonalCurrentStreak, TonalDailyMetrics, TonalDailySchedule, TonalDeviceRegistration, TonalEarnedAchievement, TonalGoal, TonalGoalMetric, TonalHomeCalendar, TonalMetricScore, TonalMetricScoresResponse, TonalMovement, TonalMuscleReadiness, TonalMuscleUtilization, TonalProgram, TonalProgramTrainingEffectGoal, TonalProgramWorkout, TonalSharedWorkout, TonalTargetScore, TonalTargetScoresResponse, TonalTrainingEffectGoal, TonalTrainingEffectGoalRelation, TonalTrainingEffectGoalsResponse, TonalTrainingType, TonalUserDevice, TonalUserGoal, TonalUserInfo, TonalUserPermissions, TonalUserSettings, TonalUserStatistics, TonalWorkout, TonalWorkoutCreateRequest, TonalWorkoutEstimateResponse, TonalWorkoutEstimateSet, TonalWorkoutSummaryData, TonalWorkoutUpdateRequest, WorkoutPublishState, WorkoutSet, TonalClient as default };
|
|
709
|
+
export { MuscleGroup, OAuthTokenResponse, TonalAchievementCategory, TonalAchievementDefinition, TonalAchievementMilestone, TonalAchievementStats, TonalActivitySummary, TonalApiError, TonalCalendarTile, TonalClient, TonalClientError, TonalCompatibilityStatus, TonalCurrentStreak, TonalDailyMetrics, TonalDailySchedule, TonalDeviceRegistration, TonalEarnedAchievement, TonalGoal, TonalGoalMetric, TonalHomeCalendar, TonalMetricScore, TonalMetricScoresResponse, TonalMovement, TonalMuscleReadiness, TonalMuscleUtilization, TonalProgram, TonalProgramTrainingEffectGoal, TonalProgramWorkout, TonalSharedWorkout, TonalStrengthBodyRegion, TonalStrengthScore, TonalStrengthScoreHistoryEntry, TonalStrengthScoreHistoryLookback, TonalTargetScore, TonalTargetScoresResponse, TonalTrainingEffectGoal, TonalTrainingEffectGoalRelation, TonalTrainingEffectGoalsResponse, TonalTrainingType, TonalUserDevice, TonalUserGoal, TonalUserInfo, TonalUserPermissions, TonalUserSettings, TonalUserStatistics, TonalWorkout, TonalWorkoutCreateRequest, TonalWorkoutEstimateResponse, TonalWorkoutEstimateSet, TonalWorkoutSummaryData, TonalWorkoutUpdateRequest, WorkoutPublishState, WorkoutSet, TonalClient as default };
|
package/dist/index.esm.js
CHANGED
|
@@ -514,6 +514,16 @@ class UserService {
|
|
|
514
514
|
async getCurrentStreak(userId) {
|
|
515
515
|
return this.httpClient.request(`/users/${userId}/streaks/current`);
|
|
516
516
|
}
|
|
517
|
+
async getCurrentStrengthScores(userId) {
|
|
518
|
+
return this.httpClient.request(`/users/${userId}/strength-scores/current`);
|
|
519
|
+
}
|
|
520
|
+
async getStrengthScoreHistory(userId, days) {
|
|
521
|
+
if (!Number.isSafeInteger(days) || days <= 0) {
|
|
522
|
+
throw new TonalClientError('Strength score history days must be a positive safe integer');
|
|
523
|
+
}
|
|
524
|
+
// Tonal's "limit" query parameter is a calendar-day window; small values return an empty array.
|
|
525
|
+
return this.httpClient.request(`/users/${userId}/strength-scores/history?limit=${days}`);
|
|
526
|
+
}
|
|
517
527
|
async getActivitySummaries(userId) {
|
|
518
528
|
return this.httpClient.request(`/users/${userId}/activity-summaries`);
|
|
519
529
|
}
|
|
@@ -619,6 +629,34 @@ class TonalClient {
|
|
|
619
629
|
const userInfo = await this.getUserInfo();
|
|
620
630
|
return this.userService.getCurrentStreak(userInfo.id);
|
|
621
631
|
}
|
|
632
|
+
async getCurrentStrengthScores() {
|
|
633
|
+
const userInfo = await this.getUserInfo();
|
|
634
|
+
return this.userService.getCurrentStrengthScores(userInfo.id);
|
|
635
|
+
}
|
|
636
|
+
async getStrengthScoreHistory(days = 'all') {
|
|
637
|
+
if (days !== 'all' && (!Number.isSafeInteger(days) || days <= 0)) {
|
|
638
|
+
throw new TonalClientError('Strength score history days must be a positive safe integer');
|
|
639
|
+
}
|
|
640
|
+
const userInfo = await this.getUserInfo();
|
|
641
|
+
let lookbackDays;
|
|
642
|
+
if (days === 'all') {
|
|
643
|
+
const createdAt = userInfo.createdAt;
|
|
644
|
+
const createdAtMs = typeof createdAt === 'string' ? Date.parse(createdAt) : Number.NaN;
|
|
645
|
+
const now = Date.now();
|
|
646
|
+
if (!Number.isFinite(createdAtMs) || createdAtMs > now) {
|
|
647
|
+
throw new TonalClientError('Cannot derive all strength score history from user createdAt; pass explicit days');
|
|
648
|
+
}
|
|
649
|
+
// +2 covers the account-creation calendar day and the server's midnight-boundary
|
|
650
|
+
// ambiguity; over-fetching before account creation is harmless. The Math.max floor is
|
|
651
|
+
// belt-and-braces only -- the future-createdAt rejection above already guarantees a
|
|
652
|
+
// non-negative difference, so this expression is always >= 2.
|
|
653
|
+
lookbackDays = Math.max(1, Math.ceil((now - createdAtMs) / 86400000) + 2);
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
lookbackDays = days;
|
|
657
|
+
}
|
|
658
|
+
return this.userService.getStrengthScoreHistory(userInfo.id, lookbackDays);
|
|
659
|
+
}
|
|
622
660
|
async getActivitySummaries() {
|
|
623
661
|
const userInfo = await this.getUserInfo();
|
|
624
662
|
return this.userService.getActivitySummaries(userInfo.id);
|