whoop-cli 1.2.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.
Files changed (52) hide show
  1. package/.env.example +4 -0
  2. package/LICENSE +21 -0
  3. package/README.md +182 -0
  4. package/dist/api/client.d.ts +19 -0
  5. package/dist/api/client.d.ts.map +1 -0
  6. package/dist/api/client.js +99 -0
  7. package/dist/api/client.js.map +1 -0
  8. package/dist/api/endpoints.d.ts +10 -0
  9. package/dist/api/endpoints.d.ts.map +1 -0
  10. package/dist/api/endpoints.js +10 -0
  11. package/dist/api/endpoints.js.map +1 -0
  12. package/dist/auth/oauth.d.ts +9 -0
  13. package/dist/auth/oauth.d.ts.map +1 -0
  14. package/dist/auth/oauth.js +122 -0
  15. package/dist/auth/oauth.js.map +1 -0
  16. package/dist/auth/server.d.ts +7 -0
  17. package/dist/auth/server.d.ts.map +1 -0
  18. package/dist/auth/server.js +53 -0
  19. package/dist/auth/server.js.map +1 -0
  20. package/dist/auth/tokens.d.ts +12 -0
  21. package/dist/auth/tokens.d.ts.map +1 -0
  22. package/dist/auth/tokens.js +102 -0
  23. package/dist/auth/tokens.js.map +1 -0
  24. package/dist/cli.d.ts +3 -0
  25. package/dist/cli.d.ts.map +1 -0
  26. package/dist/cli.js +251 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/index.d.ts +3 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +6 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/types/whoop.d.ts +159 -0
  33. package/dist/types/whoop.d.ts.map +1 -0
  34. package/dist/types/whoop.js +2 -0
  35. package/dist/types/whoop.js.map +1 -0
  36. package/dist/utils/analysis.d.ts +30 -0
  37. package/dist/utils/analysis.d.ts.map +1 -0
  38. package/dist/utils/analysis.js +231 -0
  39. package/dist/utils/analysis.js.map +1 -0
  40. package/dist/utils/date.d.ts +10 -0
  41. package/dist/utils/date.d.ts.map +1 -0
  42. package/dist/utils/date.js +38 -0
  43. package/dist/utils/date.js.map +1 -0
  44. package/dist/utils/errors.d.ts +14 -0
  45. package/dist/utils/errors.d.ts.map +1 -0
  46. package/dist/utils/errors.js +36 -0
  47. package/dist/utils/errors.js.map +1 -0
  48. package/dist/utils/format.d.ts +14 -0
  49. package/dist/utils/format.d.ts.map +1 -0
  50. package/dist/utils/format.js +241 -0
  51. package/dist/utils/format.js.map +1 -0
  52. package/package.json +58 -0
@@ -0,0 +1,231 @@
1
+ function calcStats(values) {
2
+ if (values.length === 0)
3
+ return null;
4
+ const current = values[0];
5
+ const avg = values.reduce((a, b) => a + b, 0) / values.length;
6
+ const recentAvg = values.slice(0, Math.min(3, values.length)).reduce((a, b) => a + b, 0) / Math.min(3, values.length);
7
+ const olderAvg = values.length > 3
8
+ ? values.slice(3).reduce((a, b) => a + b, 0) / values.slice(3).length
9
+ : recentAvg;
10
+ const diff = recentAvg - olderAvg;
11
+ const threshold = avg * 0.05;
12
+ return {
13
+ avg: Math.round(avg * 10) / 10,
14
+ min: Math.min(...values),
15
+ max: Math.max(...values),
16
+ current,
17
+ trend: diff > threshold ? 'up' : diff < -threshold ? 'down' : 'stable',
18
+ values,
19
+ };
20
+ }
21
+ export function analyzeTrends(recovery, sleep, cycle, period) {
22
+ const sortedRecovery = [...recovery].sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime());
23
+ const sortedSleep = [...sleep].sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime());
24
+ const sortedCycle = [...cycle].sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime());
25
+ const recoveryScores = sortedRecovery
26
+ .filter(r => r.score?.recovery_score != null)
27
+ .map(r => r.score.recovery_score);
28
+ const hrvValues = sortedRecovery
29
+ .filter(r => r.score?.hrv_rmssd_milli != null)
30
+ .map(r => r.score.hrv_rmssd_milli);
31
+ const rhrValues = sortedRecovery
32
+ .filter(r => r.score?.resting_heart_rate != null)
33
+ .map(r => r.score.resting_heart_rate);
34
+ const sleepPerf = sortedSleep
35
+ .filter(s => s.score?.sleep_performance_percentage != null && !s.nap)
36
+ .map(s => s.score.sleep_performance_percentage);
37
+ const sleepHours = sortedSleep
38
+ .filter(s => s.score?.stage_summary?.total_in_bed_time_milli != null && !s.nap)
39
+ .map(s => s.score.stage_summary.total_in_bed_time_milli / 3600000);
40
+ const strainValues = sortedCycle
41
+ .filter(c => c.score?.strain != null)
42
+ .map(c => c.score.strain);
43
+ return {
44
+ period,
45
+ recovery: calcStats(recoveryScores),
46
+ hrv: calcStats(hrvValues),
47
+ rhr: calcStats(rhrValues),
48
+ sleepPerformance: calcStats(sleepPerf),
49
+ sleepHours: calcStats(sleepHours),
50
+ strain: calcStats(strainValues),
51
+ };
52
+ }
53
+ export function generateInsights(recovery, sleep, cycle, workout) {
54
+ const insights = [];
55
+ const sortedRecovery = [...recovery].sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime());
56
+ const sortedSleep = [...sleep].sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime());
57
+ const sortedCycle = [...cycle].sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime());
58
+ const today = sortedRecovery[0]?.score;
59
+ const todaySleep = sortedSleep.find(s => !s.nap)?.score;
60
+ const todayCycle = sortedCycle[0]?.score;
61
+ if (today) {
62
+ if (today.recovery_score >= 67) {
63
+ insights.push({
64
+ category: 'recovery',
65
+ level: 'good',
66
+ title: 'Green Recovery',
67
+ message: `Recovery at ${today.recovery_score}% — body is primed for high strain.`,
68
+ action: 'Great day for intense training or competition.',
69
+ });
70
+ }
71
+ else if (today.recovery_score >= 34) {
72
+ insights.push({
73
+ category: 'recovery',
74
+ level: 'warning',
75
+ title: 'Yellow Recovery',
76
+ message: `Recovery at ${today.recovery_score}% — moderate readiness.`,
77
+ action: 'Consider moderate activity. Avoid max efforts.',
78
+ });
79
+ }
80
+ else {
81
+ insights.push({
82
+ category: 'recovery',
83
+ level: 'critical',
84
+ title: 'Red Recovery',
85
+ message: `Recovery at ${today.recovery_score}% — body needs rest.`,
86
+ action: 'Prioritize rest, hydration, and sleep tonight.',
87
+ });
88
+ }
89
+ const hrvRecords = sortedRecovery.slice(0, 7).filter(r => r.score?.hrv_rmssd_milli);
90
+ const avgHrv = hrvRecords.length > 0
91
+ ? hrvRecords.reduce((a, r) => a + r.score.hrv_rmssd_milli, 0) / hrvRecords.length
92
+ : 0;
93
+ if (avgHrv > 0 && today.hrv_rmssd_milli < avgHrv * 0.8) {
94
+ insights.push({
95
+ category: 'hrv',
96
+ level: 'warning',
97
+ title: 'HRV Below Baseline',
98
+ message: `Today's HRV (${today.hrv_rmssd_milli.toFixed(0)}ms) is ${((1 - today.hrv_rmssd_milli / avgHrv) * 100).toFixed(0)}% below your 7-day average.`,
99
+ action: 'Possible stress, poor sleep, or overtraining. Monitor closely.',
100
+ });
101
+ }
102
+ else if (avgHrv > 0 && today.hrv_rmssd_milli > avgHrv * 1.1) {
103
+ insights.push({
104
+ category: 'hrv',
105
+ level: 'good',
106
+ title: 'HRV Above Baseline',
107
+ message: `Today's HRV (${today.hrv_rmssd_milli.toFixed(0)}ms) is ${((today.hrv_rmssd_milli / avgHrv - 1) * 100).toFixed(0)}% above your 7-day average.`,
108
+ action: 'Excellent recovery. Good day for peak performance.',
109
+ });
110
+ }
111
+ }
112
+ if (todaySleep) {
113
+ const sleepDebt = todaySleep.sleep_needed.need_from_sleep_debt_milli / 3600000;
114
+ if (sleepDebt > 2) {
115
+ insights.push({
116
+ category: 'sleep',
117
+ level: 'critical',
118
+ title: 'Significant Sleep Debt',
119
+ message: `You have ${sleepDebt.toFixed(1)} hours of accumulated sleep debt.`,
120
+ action: 'Try to get to bed 30-60 min earlier for the next few days.',
121
+ });
122
+ }
123
+ else if (sleepDebt > 1) {
124
+ insights.push({
125
+ category: 'sleep',
126
+ level: 'warning',
127
+ title: 'Mild Sleep Debt',
128
+ message: `You have ${sleepDebt.toFixed(1)} hours of sleep debt.`,
129
+ action: 'Consider an earlier bedtime tonight.',
130
+ });
131
+ }
132
+ if (todaySleep.sleep_efficiency_percentage < 85) {
133
+ insights.push({
134
+ category: 'sleep',
135
+ level: 'warning',
136
+ title: 'Low Sleep Efficiency',
137
+ message: `Sleep efficiency at ${todaySleep.sleep_efficiency_percentage.toFixed(0)}% (target: 85%+).`,
138
+ action: 'Limit screen time before bed. Keep room cool and dark.',
139
+ });
140
+ }
141
+ const actualSleepTime = todaySleep.stage_summary.total_in_bed_time_milli - todaySleep.stage_summary.total_awake_time_milli;
142
+ const remPct = actualSleepTime > 0
143
+ ? (todaySleep.stage_summary.total_rem_sleep_time_milli / actualSleepTime) * 100
144
+ : 0;
145
+ if (remPct > 0 && remPct < 15) {
146
+ insights.push({
147
+ category: 'sleep',
148
+ level: 'warning',
149
+ title: 'Low REM Sleep',
150
+ message: `REM was only ${remPct.toFixed(0)}% of sleep (target: 20-25%).`,
151
+ action: 'Avoid alcohol and late meals. Maintain consistent wake time.',
152
+ });
153
+ }
154
+ }
155
+ if (todayCycle && today) {
156
+ const optimalStrain = today.recovery_score >= 67 ? 14
157
+ : today.recovery_score >= 34 ? 10
158
+ : 6;
159
+ const remaining = optimalStrain - todayCycle.strain;
160
+ if (remaining > 2) {
161
+ insights.push({
162
+ category: 'strain',
163
+ level: 'good',
164
+ title: 'Strain Capacity Available',
165
+ message: `Current strain: ${todayCycle.strain.toFixed(1)}. Optimal target: ~${optimalStrain}.`,
166
+ action: `Room for ${remaining.toFixed(1)} more strain today.`,
167
+ });
168
+ }
169
+ else if (todayCycle.strain > optimalStrain + 2) {
170
+ insights.push({
171
+ category: 'strain',
172
+ level: 'warning',
173
+ title: 'Strain Exceeds Optimal',
174
+ message: `Strain (${todayCycle.strain.toFixed(1)}) is above optimal (${optimalStrain}) for your recovery.`,
175
+ action: 'Wind down. Focus on recovery for the rest of the day.',
176
+ });
177
+ }
178
+ }
179
+ if (workout.length === 0 && today?.recovery_score >= 67) {
180
+ insights.push({
181
+ category: 'strain',
182
+ level: 'good',
183
+ title: 'No Workout Yet',
184
+ message: 'High recovery day with no recorded workout.',
185
+ action: 'Great opportunity for an intense session.',
186
+ });
187
+ }
188
+ return insights;
189
+ }
190
+ export function formatTrends(data, pretty) {
191
+ if (!pretty)
192
+ return JSON.stringify(data, null, 2);
193
+ const arrow = (t) => t === 'up' ? '↑' : t === 'down' ? '↓' : '→';
194
+ const lines = [`📊 ${data.period}-Day Trends`, ''];
195
+ if (data.recovery) {
196
+ lines.push(`💚 Recovery: ${data.recovery.avg}% avg (${data.recovery.min}-${data.recovery.max}) ${arrow(data.recovery.trend)}`);
197
+ }
198
+ if (data.hrv) {
199
+ lines.push(`💓 HRV: ${data.hrv.avg}ms avg (${data.hrv.min.toFixed(0)}-${data.hrv.max.toFixed(0)}) ${arrow(data.hrv.trend)}`);
200
+ }
201
+ if (data.rhr) {
202
+ lines.push(`❤️ RHR: ${data.rhr.avg}bpm avg (${data.rhr.min}-${data.rhr.max}) ${arrow(data.rhr.trend)}`);
203
+ }
204
+ if (data.sleepPerformance) {
205
+ lines.push(`😴 Sleep: ${data.sleepPerformance.avg}% avg (${data.sleepPerformance.min}-${data.sleepPerformance.max}) ${arrow(data.sleepPerformance.trend)}`);
206
+ }
207
+ if (data.sleepHours) {
208
+ lines.push(`🛏️ Hours: ${data.sleepHours.avg.toFixed(1)}h avg (${data.sleepHours.min.toFixed(1)}-${data.sleepHours.max.toFixed(1)}) ${arrow(data.sleepHours.trend)}`);
209
+ }
210
+ if (data.strain) {
211
+ lines.push(`🔥 Strain: ${data.strain.avg.toFixed(1)} avg (${data.strain.min.toFixed(1)}-${data.strain.max.toFixed(1)}) ${arrow(data.strain.trend)}`);
212
+ }
213
+ return lines.join('\n');
214
+ }
215
+ export function formatInsights(insights, pretty) {
216
+ if (!pretty)
217
+ return JSON.stringify(insights, null, 2);
218
+ if (insights.length === 0)
219
+ return '✅ No actionable insights — all metrics look healthy!';
220
+ const icon = (level) => level === 'good' ? '✅' : level === 'warning' ? '⚠️' : '🔴';
221
+ const lines = ['💡 Insights & Recommendations', ''];
222
+ for (const i of insights) {
223
+ lines.push(`${icon(i.level)} ${i.title}`);
224
+ lines.push(` ${i.message}`);
225
+ if (i.action)
226
+ lines.push(` → ${i.action}`);
227
+ lines.push('');
228
+ }
229
+ return lines.join('\n').trim();
230
+ }
231
+ //# sourceMappingURL=analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.js","sourceRoot":"","sources":["../../src/utils/analysis.ts"],"names":[],"mappings":"AA6BA,SAAS,SAAS,CAAC,MAAgB;IACjC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IAC9D,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACtH,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC;QAChC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM;QACrE,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,IAAI,GAAG,SAAS,GAAG,QAAQ,CAAC;IAClC,MAAM,SAAS,GAAG,GAAG,GAAG,IAAI,CAAC;IAE7B,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE;QAC9B,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QACxB,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QACxB,OAAO;QACP,KAAK,EAAE,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;QACtE,MAAM;KACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,QAAyB,EACzB,KAAmB,EACnB,KAAmB,EACnB,MAAc;IAEd,MAAM,cAAc,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACjD,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAC9E,CAAC;IACF,MAAM,WAAW,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC3C,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAC9E,CAAC;IACF,MAAM,WAAW,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC3C,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAC9E,CAAC;IAEF,MAAM,cAAc,GAAG,cAAc;SAClC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,cAAc,IAAI,IAAI,CAAC;SAC5C,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAEpC,MAAM,SAAS,GAAG,cAAc;SAC7B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,eAAe,IAAI,IAAI,CAAC;SAC7C,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAErC,MAAM,SAAS,GAAG,cAAc;SAC7B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,kBAAkB,IAAI,IAAI,CAAC;SAChD,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAExC,MAAM,SAAS,GAAG,WAAW;SAC1B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,4BAA4B,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;SACpE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAElD,MAAM,UAAU,GAAG,WAAW;SAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE,uBAAuB,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;SAC9E,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,uBAAuB,GAAG,OAAO,CAAC,CAAC;IAErE,MAAM,YAAY,GAAG,WAAW;SAC7B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,IAAI,IAAI,CAAC;SACpC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAE5B,OAAO;QACL,MAAM;QACN,QAAQ,EAAE,SAAS,CAAC,cAAc,CAAC;QACnC,GAAG,EAAE,SAAS,CAAC,SAAS,CAAC;QACzB,GAAG,EAAE,SAAS,CAAC,SAAS,CAAC;QACzB,gBAAgB,EAAE,SAAS,CAAC,SAAS,CAAC;QACtC,UAAU,EAAE,SAAS,CAAC,UAAU,CAAC;QACjC,MAAM,EAAE,SAAS,CAAC,YAAY,CAAC;KAChC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,QAAyB,EACzB,KAAmB,EACnB,KAAmB,EACnB,OAAuB;IAEvB,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,MAAM,cAAc,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACjD,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAC9E,CAAC;IACF,MAAM,WAAW,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC3C,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAC9E,CAAC;IACF,MAAM,WAAW,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC3C,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAC9E,CAAC;IAEF,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC;IACvC,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC;IACxD,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC;IAEzC,IAAI,KAAK,EAAE,CAAC;QACV,IAAI,KAAK,CAAC,cAAc,IAAI,EAAE,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,UAAU;gBACpB,KAAK,EAAE,MAAM;gBACb,KAAK,EAAE,gBAAgB;gBACvB,OAAO,EAAE,eAAe,KAAK,CAAC,cAAc,qCAAqC;gBACjF,MAAM,EAAE,gDAAgD;aACzD,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,KAAK,CAAC,cAAc,IAAI,EAAE,EAAE,CAAC;YACtC,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,UAAU;gBACpB,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,iBAAiB;gBACxB,OAAO,EAAE,eAAe,KAAK,CAAC,cAAc,yBAAyB;gBACrE,MAAM,EAAE,gDAAgD;aACzD,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,UAAU;gBACpB,KAAK,EAAE,UAAU;gBACjB,KAAK,EAAE,cAAc;gBACrB,OAAO,EAAE,eAAe,KAAK,CAAC,cAAc,sBAAsB;gBAClE,MAAM,EAAE,gDAAgD;aACzD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;QACpF,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC;YAClC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM;YACjF,CAAC,CAAC,CAAC,CAAC;QAEN,IAAI,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,eAAe,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC;YACvD,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,KAAK;gBACf,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,oBAAoB;gBAC3B,OAAO,EAAE,gBAAgB,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,eAAe,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B;gBACvJ,MAAM,EAAE,gEAAgE;aACzE,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,eAAe,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC;YAC9D,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,KAAK;gBACf,KAAK,EAAE,MAAM;gBACb,KAAK,EAAE,oBAAoB;gBAC3B,OAAO,EAAE,gBAAgB,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,eAAe,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B;gBACvJ,MAAM,EAAE,oDAAoD;aAC7D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,SAAS,GAAG,UAAU,CAAC,YAAY,CAAC,0BAA0B,GAAG,OAAO,CAAC;QAC/E,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,UAAU;gBACjB,KAAK,EAAE,wBAAwB;gBAC/B,OAAO,EAAE,YAAY,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,mCAAmC;gBAC5E,MAAM,EAAE,4DAA4D;aACrE,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YACzB,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,iBAAiB;gBACxB,OAAO,EAAE,YAAY,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,uBAAuB;gBAChE,MAAM,EAAE,sCAAsC;aAC/C,CAAC,CAAC;QACL,CAAC;QAED,IAAI,UAAU,CAAC,2BAA2B,GAAG,EAAE,EAAE,CAAC;YAChD,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,sBAAsB;gBAC7B,OAAO,EAAE,uBAAuB,UAAU,CAAC,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB;gBACpG,MAAM,EAAE,wDAAwD;aACjE,CAAC,CAAC;QACL,CAAC;QAED,MAAM,eAAe,GAAG,UAAU,CAAC,aAAa,CAAC,uBAAuB,GAAG,UAAU,CAAC,aAAa,CAAC,sBAAsB,CAAC;QAC3H,MAAM,MAAM,GAAG,eAAe,GAAG,CAAC;YAChC,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,0BAA0B,GAAG,eAAe,CAAC,GAAG,GAAG;YAC/E,CAAC,CAAC,CAAC,CAAC;QACN,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,EAAE,EAAE,CAAC;YAC9B,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,OAAO;gBACjB,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,eAAe;gBACtB,OAAO,EAAE,gBAAgB,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B;gBACxE,MAAM,EAAE,8DAA8D;aACvE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,UAAU,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,aAAa,GAAG,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;YACnD,CAAC,CAAC,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;gBACjC,CAAC,CAAC,CAAC,CAAC;QAEN,MAAM,SAAS,GAAG,aAAa,GAAG,UAAU,CAAC,MAAM,CAAC;QACpD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,QAAQ;gBAClB,KAAK,EAAE,MAAM;gBACb,KAAK,EAAE,2BAA2B;gBAClC,OAAO,EAAE,mBAAmB,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,aAAa,GAAG;gBAC9F,MAAM,EAAE,YAAY,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB;aAC9D,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,UAAU,CAAC,MAAM,GAAG,aAAa,GAAG,CAAC,EAAE,CAAC;YACjD,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,QAAQ;gBAClB,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,wBAAwB;gBAC/B,OAAO,EAAE,WAAW,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,uBAAuB,aAAa,sBAAsB;gBAC1G,MAAM,EAAE,uDAAuD;aAChE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,EAAE,cAAc,IAAI,EAAE,EAAE,CAAC;QACxD,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,MAAM;YACb,KAAK,EAAE,gBAAgB;YACvB,OAAO,EAAE,6CAA6C;YACtD,MAAM,EAAE,2CAA2C;SACpD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAe,EAAE,MAAe;IAC3D,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAElD,MAAM,KAAK,GAAG,CAAC,CAA2B,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAC3F,MAAM,KAAK,GAAa,CAAC,MAAM,IAAI,CAAC,MAAM,aAAa,EAAE,EAAE,CAAC,CAAC;IAE7D,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,QAAQ,CAAC,GAAG,UAAU,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,KAAK,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACjI,CAAC;IACD,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,GAAG,CAAC,GAAG,WAAW,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/H,CAAC;IACD,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,GAAG,CAAC,GAAG,YAAY,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1G,CAAC;IACD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,gBAAgB,CAAC,GAAG,UAAU,IAAI,CAAC,gBAAgB,CAAC,GAAG,IAAI,IAAI,CAAC,gBAAgB,CAAC,GAAG,KAAK,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9J,CAAC;IACD,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxK,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACvJ,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAmB,EAAE,MAAe;IACjE,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAEtD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,sDAAsD,CAAC;IAEzF,MAAM,IAAI,GAAG,CAAC,KAAsC,EAAE,EAAE,CACtD,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAE7D,MAAM,KAAK,GAAa,CAAC,+BAA+B,EAAE,EAAE,CAAC,CAAC;IAE9D,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1C,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export declare function getWhoopDay(date?: string): string;
2
+ export declare function formatDate(date: Date): string;
3
+ export declare function getDateRange(date: string): {
4
+ start: string;
5
+ end: string;
6
+ };
7
+ export declare function validateISODate(date: string): boolean;
8
+ export declare function nowISO(): string;
9
+ export declare function getDaysAgo(days: number): string;
10
+ //# sourceMappingURL=date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE7C;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAYzE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMrD;AAED,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI/C"}
@@ -0,0 +1,38 @@
1
+ const WHOOP_DAY_START_HOUR = 4;
2
+ export function getWhoopDay(date) {
3
+ const d = date ? new Date(date) : new Date();
4
+ if (d.getHours() < WHOOP_DAY_START_HOUR) {
5
+ d.setDate(d.getDate() - 1);
6
+ }
7
+ return formatDate(d);
8
+ }
9
+ export function formatDate(date) {
10
+ return date.toISOString().split('T')[0];
11
+ }
12
+ export function getDateRange(date) {
13
+ const d = new Date(date);
14
+ const start = new Date(d);
15
+ start.setHours(WHOOP_DAY_START_HOUR, 0, 0, 0);
16
+ const end = new Date(start);
17
+ end.setDate(end.getDate() + 1);
18
+ return {
19
+ start: start.toISOString(),
20
+ end: end.toISOString(),
21
+ };
22
+ }
23
+ export function validateISODate(date) {
24
+ const regex = /^\d{4}-\d{2}-\d{2}$/;
25
+ if (!regex.test(date))
26
+ return false;
27
+ const d = new Date(date);
28
+ return !isNaN(d.getTime());
29
+ }
30
+ export function nowISO() {
31
+ return new Date().toISOString();
32
+ }
33
+ export function getDaysAgo(days) {
34
+ const d = new Date();
35
+ d.setDate(d.getDate() - days);
36
+ return formatDate(d);
37
+ }
38
+ //# sourceMappingURL=date.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.js","sourceRoot":"","sources":["../../src/utils/date.ts"],"names":[],"mappings":"AAAA,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAE/B,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAE7C,IAAI,CAAC,CAAC,QAAQ,EAAE,GAAG,oBAAoB,EAAE,CAAC;QACxC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAU;IACnC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,KAAK,CAAC,QAAQ,CAAC,oBAAoB,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAE9C,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAE/B,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE;QAC1B,GAAG,EAAE,GAAG,CAAC,WAAW,EAAE;KACvB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,KAAK,GAAG,qBAAqB,CAAC;IACpC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAEpC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,MAAM;IACpB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;IACrB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IAC9B,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,14 @@
1
+ export declare enum ExitCode {
2
+ SUCCESS = 0,
3
+ GENERAL_ERROR = 1,
4
+ AUTH_ERROR = 2,
5
+ RATE_LIMIT = 3,
6
+ NETWORK_ERROR = 4
7
+ }
8
+ export declare class WhoopError extends Error {
9
+ code: ExitCode;
10
+ statusCode?: number | undefined;
11
+ constructor(message: string, code: ExitCode, statusCode?: number | undefined);
12
+ }
13
+ export declare function handleError(error: unknown): never;
14
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,oBAAY,QAAQ;IAClB,OAAO,IAAI;IACX,aAAa,IAAI;IACjB,UAAU,IAAI;IACd,UAAU,IAAI;IACd,aAAa,IAAI;CAClB;AAED,qBAAa,UAAW,SAAQ,KAAK;IAG1B,IAAI,EAAE,QAAQ;IACd,UAAU,CAAC,EAAE,MAAM;gBAF1B,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,QAAQ,EACd,UAAU,CAAC,EAAE,MAAM,YAAA;CAK7B;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAkBjD"}
@@ -0,0 +1,36 @@
1
+ export var ExitCode;
2
+ (function (ExitCode) {
3
+ ExitCode[ExitCode["SUCCESS"] = 0] = "SUCCESS";
4
+ ExitCode[ExitCode["GENERAL_ERROR"] = 1] = "GENERAL_ERROR";
5
+ ExitCode[ExitCode["AUTH_ERROR"] = 2] = "AUTH_ERROR";
6
+ ExitCode[ExitCode["RATE_LIMIT"] = 3] = "RATE_LIMIT";
7
+ ExitCode[ExitCode["NETWORK_ERROR"] = 4] = "NETWORK_ERROR";
8
+ })(ExitCode || (ExitCode = {}));
9
+ export class WhoopError extends Error {
10
+ code;
11
+ statusCode;
12
+ constructor(message, code, statusCode) {
13
+ super(message);
14
+ this.code = code;
15
+ this.statusCode = statusCode;
16
+ this.name = 'WhoopError';
17
+ }
18
+ }
19
+ export function handleError(error) {
20
+ if (error instanceof WhoopError) {
21
+ const status = error.statusCode ? ` (${error.statusCode})` : '';
22
+ console.error(`Error: ${error.message}${status}`);
23
+ process.exit(error.code);
24
+ }
25
+ if (error instanceof Error) {
26
+ if (error.message.includes('fetch failed') || error.message.includes('ECONNREFUSED')) {
27
+ console.error('Error: Network connection failed');
28
+ process.exit(ExitCode.NETWORK_ERROR);
29
+ }
30
+ console.error(`Error: ${error.message}`);
31
+ process.exit(ExitCode.GENERAL_ERROR);
32
+ }
33
+ console.error('Error: Unknown error occurred');
34
+ process.exit(ExitCode.GENERAL_ERROR);
35
+ }
36
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,QAMX;AAND,WAAY,QAAQ;IAClB,6CAAW,CAAA;IACX,yDAAiB,CAAA;IACjB,mDAAc,CAAA;IACd,mDAAc,CAAA;IACd,yDAAiB,CAAA;AACnB,CAAC,EANW,QAAQ,KAAR,QAAQ,QAMnB;AAED,MAAM,OAAO,UAAW,SAAQ,KAAK;IAG1B;IACA;IAHT,YACE,OAAe,EACR,IAAc,EACd,UAAmB;QAE1B,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,SAAI,GAAJ,IAAI,CAAU;QACd,eAAU,GAAV,UAAU,CAAS;QAG1B,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YACrF,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;YAClD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC/C,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { WhoopData, WhoopRecovery, WhoopSleep, WhoopCycle } from '../types/whoop.js';
2
+ import type { TrendData } from './analysis.js';
3
+ export declare function formatPretty(data: WhoopData): string;
4
+ export interface DashboardData {
5
+ today: WhoopData;
6
+ recoveryHistory: WhoopRecovery[];
7
+ sleepHistory: WhoopSleep[];
8
+ cycleHistory: WhoopCycle[];
9
+ trends: TrendData;
10
+ }
11
+ export declare function formatDashboard(d: DashboardData): string;
12
+ export declare function formatSummary(data: WhoopData): string;
13
+ export declare function formatSummaryColor(data: WhoopData): string;
14
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AA4B/C,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAiEpD;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,CAAC;IACjB,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,MAAM,EAAE,SAAS,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CA0GxD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAwBrD;AASD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CA8B1D"}
@@ -0,0 +1,241 @@
1
+ function msToHours(ms) {
2
+ return ms / 3600000;
3
+ }
4
+ function msToMin(ms) {
5
+ return ms / 60000;
6
+ }
7
+ function formatZones(zones) {
8
+ const z = [
9
+ { label: 'Z0', ms: zones.zone_zero_milli },
10
+ { label: 'Z1', ms: zones.zone_one_milli },
11
+ { label: 'Z2', ms: zones.zone_two_milli },
12
+ { label: 'Z3', ms: zones.zone_three_milli },
13
+ { label: 'Z4', ms: zones.zone_four_milli },
14
+ { label: 'Z5', ms: zones.zone_five_milli },
15
+ ];
16
+ return z.filter(x => x.ms > 0).map(x => `${x.label} ${Math.round(msToMin(x.ms))}m`).join(' | ');
17
+ }
18
+ function workoutDuration(w) {
19
+ const ms = new Date(w.end).getTime() - new Date(w.start).getTime();
20
+ const min = Math.round(ms / 60000);
21
+ return min >= 60 ? `${Math.floor(min / 60)}h${min % 60}m` : `${min}min`;
22
+ }
23
+ export function formatPretty(data) {
24
+ const lines = [];
25
+ lines.push(`📅 ${data.date}`);
26
+ lines.push('');
27
+ if (data.profile) {
28
+ lines.push(`👤 ${data.profile.first_name} ${data.profile.last_name}`);
29
+ }
30
+ if (data.body) {
31
+ const b = data.body;
32
+ lines.push(`📏 ${b.height_meter}m | ${b.weight_kilogram}kg | Max HR: ${b.max_heart_rate}`);
33
+ }
34
+ if (data.recovery?.length) {
35
+ const r = data.recovery[0].score;
36
+ lines.push(`💚 Recovery: ${r.recovery_score}% | HRV: ${r.hrv_rmssd_milli.toFixed(1)}ms | RHR: ${r.resting_heart_rate}bpm`);
37
+ const extras = [];
38
+ if (r.spo2_percentage)
39
+ extras.push(`SpO2: ${r.spo2_percentage}%`);
40
+ if (r.skin_temp_celsius != null)
41
+ extras.push(`Skin: ${r.skin_temp_celsius.toFixed(1)}°C`);
42
+ if (extras.length)
43
+ lines.push(` ${extras.join(' | ')}`);
44
+ }
45
+ if (data.sleep?.length) {
46
+ const s = data.sleep[0].score;
47
+ const ss = s.stage_summary;
48
+ const totalSleepMs = ss.total_in_bed_time_milli - ss.total_awake_time_milli;
49
+ const totalH = msToHours(ss.total_in_bed_time_milli).toFixed(1);
50
+ const deepH = msToHours(ss.total_slow_wave_sleep_time_milli).toFixed(1);
51
+ const remH = msToHours(ss.total_rem_sleep_time_milli).toFixed(1);
52
+ const lightH = msToHours(ss.total_light_sleep_time_milli).toFixed(1);
53
+ const deepPct = totalSleepMs > 0 ? Math.round((ss.total_slow_wave_sleep_time_milli / totalSleepMs) * 100) : 0;
54
+ const remPct = totalSleepMs > 0 ? Math.round((ss.total_rem_sleep_time_milli / totalSleepMs) * 100) : 0;
55
+ lines.push(`😴 Sleep: ${s.sleep_performance_percentage}% | ${totalH}h | Efficiency: ${s.sleep_efficiency_percentage.toFixed(0)}%`);
56
+ lines.push(` Deep: ${deepH}h (${deepPct}%) | REM: ${remH}h (${remPct}%) | Light: ${lightH}h`);
57
+ lines.push(` Disturbances: ${ss.disturbance_count} | Consistency: ${s.sleep_consistency_percentage}%`);
58
+ if (s.respiratory_rate)
59
+ lines.push(` Respiratory rate: ${s.respiratory_rate.toFixed(1)}/min`);
60
+ const sn = s.sleep_needed;
61
+ const debtH = msToHours(sn.need_from_sleep_debt_milli).toFixed(1);
62
+ const baselineH = msToHours(sn.baseline_milli).toFixed(1);
63
+ const needTonightMs = sn.baseline_milli + sn.need_from_sleep_debt_milli + sn.need_from_recent_strain_milli + sn.need_from_recent_nap_milli;
64
+ const needH = msToHours(needTonightMs).toFixed(1);
65
+ lines.push(` 💤 Sleep debt: ${debtH}h | Baseline: ${baselineH}h | Need tonight: ${needH}h`);
66
+ }
67
+ if (data.workout?.length) {
68
+ lines.push(`🏋️ Workouts:`);
69
+ for (const w of data.workout) {
70
+ const sc = w.score;
71
+ const dur = workoutDuration(w);
72
+ lines.push(` ${w.sport_name}: Strain ${sc.strain.toFixed(1)} | ${dur} | Avg HR: ${sc.average_heart_rate} | Max HR: ${sc.max_heart_rate} | ${(sc.kilojoule / 4.184).toFixed(0)} cal`);
73
+ if (sc.zone_durations) {
74
+ lines.push(` Zones: ${formatZones(sc.zone_durations)}`);
75
+ }
76
+ }
77
+ }
78
+ if (data.cycle?.length) {
79
+ const c = data.cycle[0].score;
80
+ lines.push(`🔄 Day strain: ${c.strain.toFixed(1)} | ${(c.kilojoule / 4.184).toFixed(0)} cal | Avg HR: ${c.average_heart_rate}`);
81
+ }
82
+ return lines.join('\n');
83
+ }
84
+ export function formatDashboard(d) {
85
+ const lines = [];
86
+ const { today, trends } = d;
87
+ // Header
88
+ const name = today.profile ? `${today.profile.first_name} ${today.profile.last_name}` : '';
89
+ lines.push(`📅 ${today.date}${name ? ` | ${name}` : ''}`);
90
+ lines.push('');
91
+ // Recovery
92
+ lines.push('── Recovery ──────────────────────────');
93
+ if (today.recovery?.length) {
94
+ const r = today.recovery[0].score;
95
+ const icon = r.recovery_score >= 67 ? '🟢' : r.recovery_score >= 34 ? '🟡' : '🔴';
96
+ const hrvAvg = trends.hrv?.avg ?? 0;
97
+ const rhrAvg = trends.rhr?.avg ?? 0;
98
+ const hrvDelta = hrvAvg > 0 ? ` (${r.hrv_rmssd_milli > hrvAvg ? '↑' : r.hrv_rmssd_milli < hrvAvg ? '↓' : '→'} vs ${hrvAvg.toFixed(0)} avg)` : '';
99
+ const rhrDelta = rhrAvg > 0 ? ` (${r.resting_heart_rate < rhrAvg ? '↓' : r.resting_heart_rate > rhrAvg ? '↑' : '→'} vs ${rhrAvg.toFixed(0)} avg)` : '';
100
+ lines.push(`${icon} ${r.recovery_score}% | HRV: ${r.hrv_rmssd_milli.toFixed(0)}ms${hrvDelta} | RHR: ${r.resting_heart_rate}bpm${rhrDelta}`);
101
+ const extras = [];
102
+ if (r.spo2_percentage)
103
+ extras.push(`SpO2: ${r.spo2_percentage}%`);
104
+ if (r.skin_temp_celsius != null)
105
+ extras.push(`Skin: ${r.skin_temp_celsius.toFixed(1)}°C`);
106
+ // Add respiratory rate from sleep data
107
+ if (today.sleep?.length && today.sleep[0].score.respiratory_rate) {
108
+ extras.push(`Resp: ${today.sleep[0].score.respiratory_rate.toFixed(1)}/min`);
109
+ }
110
+ if (extras.length)
111
+ lines.push(` ${extras.join(' | ')}`);
112
+ }
113
+ else {
114
+ lines.push(' No recovery data');
115
+ }
116
+ lines.push('');
117
+ // Sleep
118
+ lines.push('── Sleep ─────────────────────────────');
119
+ if (today.sleep?.length) {
120
+ const sl = today.sleep.find(s => !s.nap) ?? today.sleep[0];
121
+ const s = sl.score;
122
+ const ss = s.stage_summary;
123
+ const totalSleepMs = ss.total_in_bed_time_milli - ss.total_awake_time_milli;
124
+ const totalH = msToHours(ss.total_in_bed_time_milli).toFixed(1);
125
+ const deepH = msToHours(ss.total_slow_wave_sleep_time_milli).toFixed(1);
126
+ const remH = msToHours(ss.total_rem_sleep_time_milli).toFixed(1);
127
+ const lightH = msToHours(ss.total_light_sleep_time_milli).toFixed(1);
128
+ const deepPct = totalSleepMs > 0 ? Math.round((ss.total_slow_wave_sleep_time_milli / totalSleepMs) * 100) : 0;
129
+ const remPct = totalSleepMs > 0 ? Math.round((ss.total_rem_sleep_time_milli / totalSleepMs) * 100) : 0;
130
+ lines.push(`😴 ${s.sleep_performance_percentage}% | ${totalH}h total | Efficiency: ${s.sleep_efficiency_percentage.toFixed(0)}%`);
131
+ lines.push(` Deep: ${deepH}h (${deepPct}%) | REM: ${remH}h (${remPct}%) | Light: ${lightH}h`);
132
+ lines.push(` Disturbances: ${ss.disturbance_count} | Consistency: ${s.sleep_consistency_percentage}%`);
133
+ const sn = s.sleep_needed;
134
+ const debtH = msToHours(sn.need_from_sleep_debt_milli).toFixed(1);
135
+ const needTonightMs = sn.baseline_milli + sn.need_from_sleep_debt_milli + sn.need_from_recent_strain_milli + sn.need_from_recent_nap_milli;
136
+ const needH = msToHours(needTonightMs).toFixed(1);
137
+ lines.push(` 💤 Sleep debt: ${debtH}h | Need tonight: ${needH}h`);
138
+ }
139
+ else {
140
+ lines.push(' No sleep data');
141
+ }
142
+ lines.push('');
143
+ // Strain
144
+ lines.push('── Strain ────────────────────────────');
145
+ if (today.cycle?.length) {
146
+ const c = today.cycle[0].score;
147
+ const recoveryScore = today.recovery?.[0]?.score?.recovery_score ?? 50;
148
+ const optimal = recoveryScore >= 67 ? 14 : recoveryScore >= 34 ? 10 : 6;
149
+ lines.push(`🔥 ${c.strain.toFixed(1)} / ${optimal} optimal | ${(c.kilojoule / 4.184).toFixed(0)} cal`);
150
+ }
151
+ if (today.workout?.length) {
152
+ for (const w of today.workout) {
153
+ const sc = w.score;
154
+ const dur = workoutDuration(w);
155
+ lines.push(` ${w.sport_name} (strain ${sc.strain.toFixed(1)}, ${dur})`);
156
+ if (sc.zone_durations) {
157
+ lines.push(` Zones: ${formatZones(sc.zone_durations)}`);
158
+ }
159
+ }
160
+ }
161
+ else if (!today.cycle?.length) {
162
+ lines.push(' No strain data');
163
+ }
164
+ lines.push('');
165
+ // 7-Day Trends
166
+ lines.push('── 7-Day Trends ──────────────────────');
167
+ const arrow = (t) => t === 'up' ? '↑' : t === 'down' ? '↓' : '→';
168
+ if (trends.hrv) {
169
+ const oldest = trends.hrv.values[trends.hrv.values.length - 1];
170
+ lines.push(` HRV: ${oldest?.toFixed(0) ?? '?'} → ${trends.hrv.current.toFixed(0)}ms ${arrow(trends.hrv.trend)} (range ${trends.hrv.min.toFixed(0)}-${trends.hrv.max.toFixed(0)})`);
171
+ }
172
+ if (trends.rhr) {
173
+ const oldest = trends.rhr.values[trends.rhr.values.length - 1];
174
+ lines.push(` RHR: ${oldest ?? '?'} → ${trends.rhr.current}bpm ${arrow(trends.rhr.trend)} (range ${trends.rhr.min}-${trends.rhr.max})`);
175
+ }
176
+ if (trends.recovery) {
177
+ const oldest = trends.recovery.values[trends.recovery.values.length - 1];
178
+ lines.push(` Recovery: ${oldest ?? '?'} → ${trends.recovery.current}% ${arrow(trends.recovery.trend)}`);
179
+ }
180
+ if (trends.sleepHours) {
181
+ const oldest = trends.sleepHours.values[trends.sleepHours.values.length - 1];
182
+ lines.push(` Sleep: ${oldest?.toFixed(1) ?? '?'} → ${trends.sleepHours.current.toFixed(1)}h ${arrow(trends.sleepHours.trend)}`);
183
+ }
184
+ if (trends.strain) {
185
+ lines.push(` Strain: ${trends.strain.avg.toFixed(1)} avg (range ${trends.strain.min.toFixed(1)}-${trends.strain.max.toFixed(1)})`);
186
+ }
187
+ return lines.join('\n');
188
+ }
189
+ export function formatSummary(data) {
190
+ const parts = [];
191
+ if (data.recovery?.length) {
192
+ const r = data.recovery[0].score;
193
+ parts.push(`Recovery: ${r.recovery_score}%`);
194
+ parts.push(`HRV: ${r.hrv_rmssd_milli.toFixed(0)}ms`);
195
+ parts.push(`RHR: ${r.resting_heart_rate}`);
196
+ }
197
+ if (data.sleep?.length) {
198
+ const s = data.sleep[0].score;
199
+ parts.push(`Sleep: ${s.sleep_performance_percentage}%`);
200
+ }
201
+ if (data.cycle?.length) {
202
+ parts.push(`Strain: ${data.cycle[0].score.strain.toFixed(1)}`);
203
+ }
204
+ if (data.workout?.length) {
205
+ parts.push(`Workouts: ${data.workout.length}`);
206
+ }
207
+ return parts.length ? `${data.date} | ${parts.join(' | ')}` : `${data.date} | No data`;
208
+ }
209
+ function statusIcon(value, green, yellow, invert = false) {
210
+ if (invert) {
211
+ return value <= green ? '🟢' : value <= yellow ? '🟡' : '🔴';
212
+ }
213
+ return value >= green ? '🟢' : value >= yellow ? '🟡' : '🔴';
214
+ }
215
+ export function formatSummaryColor(data) {
216
+ const lines = [`📅 ${data.date}`];
217
+ if (data.recovery?.length) {
218
+ const r = data.recovery[0].score;
219
+ const icon = statusIcon(r.recovery_score, 67, 34);
220
+ lines.push(`${icon} Recovery: ${r.recovery_score}% | HRV: ${r.hrv_rmssd_milli.toFixed(0)}ms | RHR: ${r.resting_heart_rate}bpm`);
221
+ }
222
+ if (data.sleep?.length) {
223
+ const s = data.sleep[0].score;
224
+ const icon = statusIcon(s.sleep_performance_percentage, 85, 70);
225
+ const hours = (s.stage_summary.total_in_bed_time_milli / 3600000).toFixed(1);
226
+ lines.push(`${icon} Sleep: ${s.sleep_performance_percentage}% | ${hours}h | Efficiency: ${s.sleep_efficiency_percentage.toFixed(0)}%`);
227
+ }
228
+ if (data.cycle?.length) {
229
+ const c = data.cycle[0].score;
230
+ const recoveryScore = data.recovery?.[0]?.score?.recovery_score ?? 50;
231
+ const optimal = recoveryScore >= 67 ? 14 : recoveryScore >= 34 ? 10 : 6;
232
+ const diff = Math.abs(c.strain - optimal);
233
+ const icon = diff <= 2 ? '🟢' : diff <= 4 ? '🟡' : '🔴';
234
+ lines.push(`${icon} Strain: ${c.strain.toFixed(1)} (optimal: ~${optimal}) | ${(c.kilojoule / 4.184).toFixed(0)} cal`);
235
+ }
236
+ if (data.workout?.length) {
237
+ lines.push(`🏋️ Workouts: ${data.workout.length} | ${data.workout.map(w => w.sport_name).join(', ')}`);
238
+ }
239
+ return lines.join('\n');
240
+ }
241
+ //# sourceMappingURL=format.js.map