technogym-js 0.0.0-stage → 0.1.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.
@@ -0,0 +1,606 @@
1
+ /**
2
+ * Base class of every error this library throws, so `instanceof TechnogymError` catches them all.
3
+ * `name` is the concrete subclass's name.
4
+ */
5
+ declare class TechnogymError extends Error {
6
+ /** `options.cause` is the underlying error, if any. */
7
+ constructor(message: string, options?: {
8
+ cause?: unknown;
9
+ });
10
+ }
11
+ /**
12
+ * Login failed, there is no session, or mywellness rejected the session token (HTTP 401/403 or an
13
+ * auth-looking error in the response envelope). Sign in again.
14
+ */
15
+ declare class TechnogymAuthError extends TechnogymError {
16
+ }
17
+ /** Network failure or timeout, after retries. */
18
+ declare class TechnogymConnectionError extends TechnogymError {
19
+ }
20
+ /** 429. `retryAfter` is seconds, taken from the Retry-After header when present. */
21
+ declare class TechnogymRateLimitError extends TechnogymError {
22
+ /** Seconds to wait before retrying, from the `Retry-After` header; absent without one. */
23
+ readonly retryAfter?: number | undefined;
24
+ /** Thrown by the transport on a 429. */
25
+ constructor(message: string,
26
+ /** Seconds to wait before retrying, from the `Retry-After` header; absent without one. */
27
+ retryAfter?: number | undefined, options?: {
28
+ cause?: unknown;
29
+ });
30
+ }
31
+ /** Any other non-2xx response. */
32
+ declare class TechnogymHttpError extends TechnogymError {
33
+ /** The HTTP status code. */
34
+ readonly status: number;
35
+ /** The URL that was requested, without its query string. */
36
+ readonly url: string;
37
+ /** The response body, as text. */
38
+ readonly body: string;
39
+ /** Thrown by the transport for a non-2xx response that no other error class covers. */
40
+ constructor(message: string,
41
+ /** The HTTP status code. */
42
+ status: number,
43
+ /** The URL that was requested, without its query string. */
44
+ url: string,
45
+ /** The response body, as text. */
46
+ body: string, options?: {
47
+ cause?: unknown;
48
+ });
49
+ }
50
+ /** One entry of the `errors` array mywellness puts in a 200 response when a call fails. */
51
+ interface MywellnessErrorEntry {
52
+ /** Machine-readable reason, e.g. `ClientApplicationNotTrusted`. */
53
+ details?: string;
54
+ /** Human-readable message, when there is one. */
55
+ errorMessage?: string;
56
+ /** Alternative message field some endpoints use. */
57
+ message?: string;
58
+ [key: string]: unknown;
59
+ }
60
+ /**
61
+ * mywellness answered 200 with a non-empty `errors` array that is not about the session. `errors`
62
+ * is that array, untouched.
63
+ */
64
+ declare class TechnogymApiError extends TechnogymError {
65
+ /** The endpoint's last path segment, e.g. `ActivityHistory`. */
66
+ readonly action: string;
67
+ /** The envelope's `errors` array. */
68
+ readonly errors: readonly MywellnessErrorEntry[];
69
+ /** Thrown when a 200 response's envelope carries errors. */
70
+ constructor(message: string,
71
+ /** The endpoint's last path segment, e.g. `ActivityHistory`. */
72
+ action: string,
73
+ /** The envelope's `errors` array. */
74
+ errors: readonly MywellnessErrorEntry[], options?: {
75
+ cause?: unknown;
76
+ });
77
+ }
78
+ /** The requested session, exercise or analytics record does not exist. */
79
+ declare class TechnogymNotFoundError extends TechnogymError {
80
+ }
81
+
82
+ /**
83
+ * Parsed shapes of mywellness responses. Every parsed object has a `*Raw` twin method on
84
+ * `Technogym` that returns the untouched payload, for anything these types do not model.
85
+ *
86
+ * Localised strings (exercise names, units, `formatted` values) come back in the account's culture.
87
+ */
88
+ /** A gym the account belongs to. `url` is the slug every training endpoint path starts with. */
89
+ interface Facility {
90
+ id: string;
91
+ url: string;
92
+ name: string;
93
+ }
94
+ /** The signed-in account, from the login response's `userContext` and `facilities`. */
95
+ interface UserInfo {
96
+ userId: string;
97
+ firstName: string;
98
+ lastName: string;
99
+ nickName: string;
100
+ /** e.g. `Metric`. */
101
+ measurementSystem: string;
102
+ /** e.g. `nb-NO`; localised strings in every response follow it. */
103
+ culture: string;
104
+ facilities: Facility[];
105
+ }
106
+ /**
107
+ * One measured or prescribed property, normalised from mywellness's two row shapes. `value` is in
108
+ * the API's base unit (seconds, metres, kg), `displayValue` is in `unit` (the unit the console shows),
109
+ * and `formatted` is the localised display string. On CardioLog rows `value` and `displayValue`
110
+ * are the same number.
111
+ */
112
+ interface Property {
113
+ value: unknown;
114
+ displayValue: unknown;
115
+ unit: string;
116
+ formatted: unknown;
117
+ }
118
+ /** Properties keyed by their mywellness name (`Calories`, `AvgSpeed`, `IsoWeight`, ...). */
119
+ type PropertyMap = Record<string, Property>;
120
+ /** A row of `listSessions`. `sessionId` and `date` together address the session's detail calls. */
121
+ interface SessionSummary {
122
+ /** The session's `idCr`. */
123
+ sessionId: string;
124
+ /** `YYYY-MM-DD`. */
125
+ date: string;
126
+ /** `HH:MM`, local to the gym. */
127
+ time: string;
128
+ name: string;
129
+ /** `WorkoutSession`, `HRWorkout`, ... */
130
+ type: string;
131
+ durationMin: number | null;
132
+ moves: number | null;
133
+ calories: number | null;
134
+ exercisesDone: number | null;
135
+ exercisesPlanned: number | null;
136
+ userWorkoutSessionId: string;
137
+ facilityId: string;
138
+ workoutEffectiveness: number | null;
139
+ workoutCompliance: number | null;
140
+ }
141
+ /** One performed or prescribed set of an exercise. */
142
+ interface SetData {
143
+ /** 1-based. */
144
+ setNumber: number;
145
+ /** `IsoReps`, else `Reps`. */
146
+ reps: number | null;
147
+ /** `IsoWeight`, else `Weight`. */
148
+ weightKg: number | null;
149
+ restSec: number | null;
150
+ durationSec: number | null;
151
+ /** Every raw property of the step. */
152
+ properties: PropertyMap;
153
+ }
154
+ /** Allowed workload range for one property on this machine. */
155
+ interface WorkloadRange {
156
+ min: unknown;
157
+ max: unknown;
158
+ step: unknown;
159
+ readOnly: boolean;
160
+ visibility: unknown;
161
+ }
162
+ /** One exercise of a workout session. */
163
+ interface Exercise {
164
+ /** `performedPhysicalActivity.id`. */
165
+ activityId: string;
166
+ /** Pass to `getExerciseAnalytics` for the per-second data. */
167
+ analyticsId: string;
168
+ name: string;
169
+ /** The equipment's name, e.g. `Leg press #40`. */
170
+ machine: string;
171
+ equipmentType: string;
172
+ /** `StrengthLoad`, `Cardio`, ... (`physicalActivityType`). */
173
+ type: string;
174
+ isCardio: boolean;
175
+ status: string;
176
+ doneOn: string;
177
+ position: number | null;
178
+ /** Localised display strings, e.g. `6 min.`. */
179
+ duration: string;
180
+ calories: string;
181
+ moves: string;
182
+ muscles: string[];
183
+ /**
184
+ * Every summary property the machine reported (cardio: AvgSpeed, AvgRunningCadence,
185
+ * AvgRunningPower, Pace...; strength: Rm1, TotalIsoWeight...).
186
+ */
187
+ summary: PropertyMap;
188
+ sets: SetData[];
189
+ prescribedSets: SetData[];
190
+ shortName: string;
191
+ activityCode: number | null;
192
+ activityTypeId: string;
193
+ equipmentCode: number | null;
194
+ /** What the exercise was set to: `Duration`, `Distance`, `Calories`, ... */
195
+ targetType: string;
196
+ /** `Added`, `Prescribed`, ... */
197
+ doneAs: string;
198
+ manuallyDone: boolean;
199
+ hasAdjustments: boolean;
200
+ donePartially: boolean;
201
+ progress: number | null;
202
+ /** Prescribed or estimated figures, to compare with what was done. */
203
+ estimated: {
204
+ duration: unknown;
205
+ calories: unknown;
206
+ moves: unknown;
207
+ };
208
+ workloadRanges: Record<string, WorkloadRange>;
209
+ /** Non-empty console `extData`, with the `mwc_` prefix removed. */
210
+ extData: Record<string, unknown>;
211
+ }
212
+ /** Full detail of one workout session. */
213
+ interface WorkoutSession {
214
+ sessionId: string;
215
+ /** `YYYY-MM-DD`, from `startedOn`. */
216
+ date: string;
217
+ startedOn: string;
218
+ closedOn: string;
219
+ name: string;
220
+ type: string;
221
+ status: string;
222
+ duration: string;
223
+ durationPlanned: string;
224
+ calories: string;
225
+ moves: string;
226
+ efficacy: number | null;
227
+ efficacyQuality: string;
228
+ efficacyDescription: string;
229
+ /** Muscle name to score. */
230
+ muscleScores: Record<string, number>;
231
+ /** Only exercises that were performed. */
232
+ exercises: Exercise[];
233
+ fromTgCoach: boolean;
234
+ facilityId: string;
235
+ userWorkoutSessionId: string;
236
+ clientApplication: string;
237
+ /** Every session-level total. */
238
+ doneSummary: PropertyMap;
239
+ }
240
+ interface HrZone {
241
+ name: string;
242
+ lowerBound: number | null;
243
+ upperBound: number | null;
244
+ secondsInZone: number;
245
+ }
246
+ /** One second of an exercise: `t` plus one value per machine channel (`Speed`, `Power`, ...). */
247
+ interface AnalyticsSample {
248
+ t: number;
249
+ [channel: string]: number | null;
250
+ }
251
+ /** A console auto-lap (every km on a treadmill). */
252
+ interface Lap {
253
+ n: number | null;
254
+ lapDistanceM: number | null;
255
+ lapTimeSec: number | null;
256
+ totalDistanceM: number | null;
257
+ totalTimeSec: number | null;
258
+ }
259
+ /** Per-second analytics of one exercise: machine channels, heart rate and laps. */
260
+ interface ExerciseAnalytics {
261
+ analyticsId: string;
262
+ name: string;
263
+ equipmentType: string;
264
+ /** Channel names in descriptor order. Strength machines have none. */
265
+ channels: string[];
266
+ samples: AnalyticsSample[];
267
+ hr: {
268
+ t: number;
269
+ hr: number;
270
+ }[];
271
+ hrZones: HrZone[];
272
+ summary: PropertyMap;
273
+ /** As the API formats it, e.g. `09.09.2026`. */
274
+ date: string;
275
+ target: string;
276
+ activityTypeId: string;
277
+ activityName: string;
278
+ laps: Lap[];
279
+ }
280
+ /** The whole-session heart-rate trace. */
281
+ interface HrSession {
282
+ sessionId: string;
283
+ /** `YYYY-MM-DD`. */
284
+ date: string;
285
+ /** Duration, avg/min/max HR, calories, MOVEs, METs. */
286
+ summary: PropertyMap;
287
+ hrSamples: {
288
+ timestamp: string;
289
+ hr: number;
290
+ }[];
291
+ hrZones: HrZone[];
292
+ }
293
+ /** A raw payload, passed through untouched. */
294
+ type RawPayload = Record<string, unknown>;
295
+
296
+ /** A signed-in mywellness session: the bearer token and the account it belongs to. */
297
+ interface Session {
298
+ /** The bearer token sent with every request. */
299
+ token: string;
300
+ /** The account, as the login response described it. */
301
+ user: UserInfo;
302
+ /** When the session was created or its token last rotated, in epoch milliseconds. */
303
+ savedAt: number;
304
+ }
305
+ /**
306
+ * Persists a client's session between runs. Implement it to keep sessions in a database or a
307
+ * secret store; `FileSessionStore` and `MemorySessionStore` are ready-made.
308
+ */
309
+ interface SessionStore {
310
+ /** The saved session, or `null` when there is none. */
311
+ load(): Promise<Session | null>;
312
+ /** Saves a session, replacing any saved before. */
313
+ save(session: Session): Promise<void>;
314
+ /** Removes the saved session. */
315
+ clear(): Promise<void>;
316
+ }
317
+ /** Keeps the session in memory only, so it is lost when the process exits. The client's default. */
318
+ declare class MemorySessionStore implements SessionStore {
319
+ #private;
320
+ load(): Promise<Session | null>;
321
+ save(session: Session): Promise<void>;
322
+ clear(): Promise<void>;
323
+ }
324
+ /** Stores the session as `session.json` (mode 0600) in `dir`, which is created on the first save. */
325
+ declare class FileSessionStore implements SessionStore {
326
+ private readonly dir;
327
+ constructor(dir: string);
328
+ load(): Promise<Session | null>;
329
+ save(session: Session): Promise<void>;
330
+ clear(): Promise<void>;
331
+ }
332
+
333
+ /** An email and password for mywellness. */
334
+ interface Credentials {
335
+ email: string;
336
+ password: string;
337
+ }
338
+ /** Options for `new TechnogymClient(...)`. Every field is optional. */
339
+ interface TechnogymClientOptions {
340
+ /** Where the session is loaded from and saved to. Default: a `MemorySessionStore`. */
341
+ sessionStore?: SessionStore;
342
+ /**
343
+ * Sign in lazily on the first call and again whenever mywellness rejects the token. Without
344
+ * them, call `login` or `loadSession` first, and a rejected token throws `TechnogymAuthError`.
345
+ * Credentials passed to `login()` are also kept (in memory only) for the same purpose.
346
+ */
347
+ credentials?: Credentials;
348
+ /**
349
+ * Which facility's url prefixes training paths, matched against a facility's `id` or `url`.
350
+ * Default: the account's first facility.
351
+ */
352
+ facility?: string;
353
+ /** Per-request timeout in ms. Default 30000. */
354
+ timeoutMs?: number;
355
+ /** How many times a transient failure of a read is retried. Default 3. */
356
+ retries?: number;
357
+ /** Delay before the first retry in ms, doubled on each retry after it. Default 500. */
358
+ backoffMs?: number;
359
+ /** A `fetch` to use instead of the global one, e.g. a mock in tests. */
360
+ fetchImpl?: typeof fetch;
361
+ }
362
+ /** Options for one raw `services` call. */
363
+ interface ServiceOptions {
364
+ /** Per-request timeout, overriding the client's default. */
365
+ timeoutMs?: number;
366
+ /**
367
+ * Retry on a 5xx or network error. Default `true`: every mywellness read is a POST, and this
368
+ * library only reads. Pass `false` for a POST that changes something.
369
+ */
370
+ retry?: boolean;
371
+ }
372
+ /** Whether an envelope's `errors` describe a rejected session. */
373
+ declare function isAuthErrorEntry(errors: readonly MywellnessErrorEntry[]): boolean;
374
+ /**
375
+ * The authenticated HTTP transport: signs in to mywellness, keeps the session, and sends
376
+ * requests to `services.mywellness.com/{facility}`. Wrap it in a `Technogym` to call the API.
377
+ */
378
+ declare class TechnogymClient {
379
+ #private;
380
+ /** Where this client loads and saves its session. */
381
+ readonly sessionStore: SessionStore;
382
+ /** Creates a client that is not signed in yet. */
383
+ constructor(options?: TechnogymClientOptions);
384
+ /**
385
+ * Signs in with email and password and saves the session to the store. The credentials are
386
+ * kept in memory so a rejected token can be renewed without asking again. Throws
387
+ * `TechnogymAuthError` when mywellness refuses them.
388
+ */
389
+ login(email: string, password: string): Promise<Session>;
390
+ /** Loads the saved session. Returns false when the store holds none. */
391
+ loadSession(): Promise<boolean>;
392
+ /** Uses this session from now on. Does not save it to the store. */
393
+ setSession(session: Session): void;
394
+ /** The session currently held in memory, or `null` before a login or `loadSession()`. */
395
+ getSession(): Session | null;
396
+ /** Forgets the session and the in-memory credentials, and clears the store. */
397
+ logout(): Promise<void>;
398
+ /**
399
+ * The signed-in account. Signs in first when the client was given credentials and holds no
400
+ * session yet; otherwise throws `TechnogymAuthError`.
401
+ */
402
+ user(): Promise<UserInfo>;
403
+ /**
404
+ * The facility training paths are sent to (see `TechnogymClientOptions.facility`), or `null` for
405
+ * an account with no facility. Live on 2026-10-31, an account whose login listed no facilities
406
+ * got the same answers with no facility prefix as with any prefix, so paths go out unprefixed.
407
+ */
408
+ facilityOf(user: UserInfo): Facility | null;
409
+ /**
410
+ * POSTs `body` to a training endpoint and returns the envelope's `data` (or the whole body when
411
+ * it has none). `path` is relative to the facility, e.g. `/Training/User/{userId}/MyMovergy`;
412
+ * `{userId}` is replaced with the signed-in user's id. For endpoints `Technogym` has no method for.
413
+ *
414
+ * A rejected session is renewed once, when the client has credentials, and the call repeated.
415
+ */
416
+ services<T = unknown>(path: string, body?: unknown, options?: ServiceOptions): Promise<T | null>;
417
+ }
418
+
419
+ /**
420
+ * Derived views over `ExerciseAnalytics.samples`: per-channel stats and interval / lap / window
421
+ * segments. Pure functions, no HTTP. The mywellness summary block omits most machine channels
422
+ * (RunningCadence and RunningPower on a Skillrun, for one), so these fill that gap.
423
+ */
424
+ /** Channels that accumulate over the exercise: only the final value means anything, not the average. */
425
+ declare const CUMULATIVE_CHANNELS: ReadonlySet<string>;
426
+ /**
427
+ * Channels SET on the console rather than measured. A change in any of them starts a new interval
428
+ * on a treadmill or bike programme.
429
+ */
430
+ declare const SETPOINT_CHANNELS: readonly ["Speed", "Grade", "Level", "Resistance", "Incline", "Rpm_Target"];
431
+ /**
432
+ * Aggregates of one channel. Measured channels get `avg` and `minNonzero` over the non-zero
433
+ * readings (the machine reports 0 while the belt is stopped); cumulative channels get `final`.
434
+ */
435
+ interface ChannelStats {
436
+ max: number;
437
+ samples: number;
438
+ avg?: number;
439
+ minNonzero?: number;
440
+ final?: number;
441
+ }
442
+ /** A cumulative channel within one segment: the amount covered in it, and its value at the end. */
443
+ interface CumulativeDelta {
444
+ delta: number;
445
+ end: number;
446
+ }
447
+ type SegmentMode = "workload" | "laps" | "fixed";
448
+ /** `auto`: workload intervals when there are at least two, else laps, else fixed windows. */
449
+ type SegmentBy = SegmentMode | "auto";
450
+ interface Segment {
451
+ startSec: number;
452
+ endSec: number;
453
+ durationSec: number;
454
+ /** 1-based, on `workload` segments. */
455
+ interval?: number;
456
+ /** The constant setpoint values of a `workload` segment. */
457
+ setpoint?: Record<string, number>;
458
+ /** On `laps` segments. */
459
+ lap?: number | null;
460
+ lapDistanceM?: number | null;
461
+ /** From the average `Speed` (km/h), when there is one. */
462
+ paceMinPerKm?: number;
463
+ channels: Record<string, ChannelStats | CumulativeDelta>;
464
+ }
465
+ interface SegmentOptions {
466
+ /** Default `auto`. */
467
+ segmentBy?: SegmentBy;
468
+ /** Window length for `fixed`, at least 10. Default 300. */
469
+ segmentSeconds?: number;
470
+ }
471
+ /** Per-channel aggregates over `samples`. Channels with no numeric reading are left out. */
472
+ declare function channelStats(samples: readonly AnalyticsSample[], channels: readonly string[]): Record<string, ChannelStats>;
473
+ /**
474
+ * Splits an exercise into segments and aggregates every channel per segment. Cumulative channels
475
+ * report the distance (or calories, MOVEs) covered within the segment. A segment covers samples
476
+ * with `startSec < t <= endSec`.
477
+ */
478
+ declare function segmentAnalytics(samples: readonly AnalyticsSample[], channels: readonly string[], laps: readonly Lap[], options?: SegmentOptions): {
479
+ mode: SegmentMode;
480
+ segments: Segment[];
481
+ };
482
+ /** Rows with `fromSec <= t <= toSec`; `toSec` of 0 or less means no upper bound. */
483
+ declare function windowSamples<T extends {
484
+ t: number;
485
+ }>(rows: readonly T[], fromSec?: number, toSec?: number): T[];
486
+ /** Every `step`th row, starting with the first. */
487
+ declare function downsample<T>(rows: readonly T[], step: number): T[];
488
+
489
+ /** Options for `listSessions`. */
490
+ interface ListSessionsOptions {
491
+ /**
492
+ * Only gym-floor workout sessions (`justThisType: WorkoutSession`). Default `true`; `false`
493
+ * includes every activity type (classes, outdoor, ...).
494
+ */
495
+ onlyWorkouts?: boolean;
496
+ }
497
+ /** `getExerciseAnalytics` plus the derived stats and segments of `analyzeExercise`. */
498
+ interface AnalyzedExercise extends ExerciseAnalytics {
499
+ /** Per-channel avg / max / min over the full 1 Hz data. */
500
+ channelStats: Record<string, ChannelStats>;
501
+ segmentMode: SegmentMode;
502
+ segments: Segment[];
503
+ }
504
+ /**
505
+ * The Technogym mywellness API, one method per endpoint. Wraps a `TechnogymClient`:
506
+ *
507
+ * ```ts
508
+ * const client = new TechnogymClient({ sessionStore: new FileSessionStore("./session") });
509
+ * await client.login(email, password);
510
+ * const technogym = new Technogym(client);
511
+ * const sessions = await technogym.listSessions("2026-09-01", "2026-09-30");
512
+ * ```
513
+ *
514
+ * A session is addressed by its `sessionId` AND its date: the API partitions sessions by day.
515
+ * Dates accept a `YYYY-MM-DD` string or a `Date` (its local calendar day). Read-only: there is
516
+ * no method that changes anything on the account.
517
+ */
518
+ declare class Technogym {
519
+ #private;
520
+ /** The client every request goes through. */
521
+ readonly client: TechnogymClient;
522
+ /** Wraps a client. It signs in on first use when it was given credentials. */
523
+ constructor(
524
+ /** The client every request goes through. */
525
+ client: TechnogymClient);
526
+ /** The signed-in account: user id, name, culture and facilities. No HTTP call once signed in. */
527
+ getUser(): Promise<UserInfo>;
528
+ /** The account's current Movergy score, or `null` when mywellness has none. */
529
+ getMovergy(): Promise<number | null>;
530
+ /** The untouched `ActivityHistory` payload for a date range (bounds in either order). */
531
+ getActivityHistoryRaw(from: string | Date, to: string | Date, options?: ListSessionsOptions): Promise<RawPayload | null>;
532
+ /** Sessions between two dates (inclusive, either order), newest first. An overview only. */
533
+ listSessions(from: string | Date, to: string | Date, options?: ListSessionsOptions): Promise<SessionSummary[]>;
534
+ /**
535
+ * The date of a session, found by scanning the last `lookbackDays` (default 365) of history
536
+ * across every activity type. Throws `TechnogymNotFoundError` when it is not there.
537
+ */
538
+ findSessionDate(sessionId: string | number, lookbackDays?: number): Promise<string>;
539
+ /** The untouched `GetPerformedWorkoutSessionByIdCr` payload. Pass `date`; see `getWorkoutSession`. */
540
+ getWorkoutSessionRaw(sessionId: string | number, date?: string | Date): Promise<RawPayload>;
541
+ /**
542
+ * Every exercise of a session with machine, sets (performed and prescribed), the machine's full
543
+ * summary and workload ranges, plus efficacy, muscle scores and totals. Summaries are averages
544
+ * and maxima only: for splits and time series, call `getExerciseAnalytics` with an exercise's
545
+ * `analyticsId`. `date` is strongly recommended; without it the last year is scanned for it.
546
+ */
547
+ getWorkoutSession(sessionId: string | number, date?: string | Date): Promise<WorkoutSession>;
548
+ /**
549
+ * Every workout session of the last `days` days (1-365, default 14), fully loaded, newest first.
550
+ * One request for the list plus one per session.
551
+ */
552
+ getRecentWorkouts(days?: number): Promise<WorkoutSession[]>;
553
+ /** The untouched `CardioLog/{analyticsId}/Details` payload. */
554
+ getExerciseAnalyticsRaw(analyticsId: string): Promise<RawPayload>;
555
+ /**
556
+ * Per-second analytics of one exercise: the machine's channels (Skillrun: Speed, Grade,
557
+ * HDistance, RunningCadence, RunningPower; bikes: Power, Rpm...), heart rate, HR zones, laps and
558
+ * summary. Strength machines have no time series; use the sets of `getWorkoutSession`.
559
+ */
560
+ getExerciseAnalytics(analyticsId: string): Promise<ExerciseAnalytics>;
561
+ /**
562
+ * `getExerciseAnalytics` plus per-channel stats and segments: by default one per interval
563
+ * detected from setpoint changes (speed / grade / level), else per console lap, else fixed
564
+ * windows. See `segmentAnalytics` to run the same analysis on samples you already have.
565
+ */
566
+ analyzeExercise(analyticsId: string, options?: SegmentOptions): Promise<AnalyzedExercise>;
567
+ /** The untouched `GetHrSession` payload. */
568
+ getSessionHeartRateRaw(sessionId: string | number, date?: string | Date): Promise<RawPayload>;
569
+ /** The whole-session heart-rate trace: timestamped samples, HR zones and avg/min/max HR. */
570
+ getSessionHeartRate(sessionId: string | number, date?: string | Date): Promise<HrSession>;
571
+ }
572
+
573
+ /** Account and login host. */
574
+ declare const CORE_URL = "https://core.mywellness.com";
575
+ /** Training-data host; every path under it starts with the facility's url slug. */
576
+ declare const SERVICES_URL = "https://services.mywellness.com";
577
+ /**
578
+ * App id and name of the official end-user web app (endusernext.mywellness.com). Without these
579
+ * headers the API answers `ClientApplicationNotTrusted`.
580
+ */
581
+ declare const APP_ID = "EC1D38D7-D359-48D0-A60C-D8C0B8FB9DF9";
582
+ declare const APP_NAME = "enduserweb";
583
+
584
+ /** The login response's account, or `null` when it has no `userContext.id`. */
585
+ declare function parseUser(data: unknown): UserInfo | null;
586
+ /** One `ActivityHistory` item. */
587
+ declare function parseSessionSummary(item: unknown): SessionSummary;
588
+ /**
589
+ * Normalises mywellness property rows. Two shapes exist: session and HR rows carry `value` in the
590
+ * base unit plus `displayValue` in `unitOfMeasureShortString`; CardioLog rows carry `rawValue`
591
+ * already in the display unit (`uM`) and `value` as a formatted string.
592
+ */
593
+ declare function parseProperties(rows: unknown): PropertyMap;
594
+ /** One `physicalActivities` entry of a session. */
595
+ declare function parseExercise(activity: unknown): Exercise;
596
+ /** A `GetPerformedWorkoutSessionByIdCr` payload. Exercises never performed are left out. */
597
+ declare function parseWorkoutSession(data: unknown, sessionId: string): WorkoutSession;
598
+ /**
599
+ * A `CardioLog/{id}/Details` payload. The API spells the block `analitics`; `analytics` is
600
+ * accepted too. Samples with an empty value vector are dropped.
601
+ */
602
+ declare function parseExerciseAnalytics(data: unknown, analyticsId: string): ExerciseAnalytics;
603
+ /** A `GetHrSession` payload. */
604
+ declare function parseHrSession(data: unknown, sessionId: string, date: string): HrSession;
605
+
606
+ export { APP_ID, APP_NAME, type AnalyticsSample, type AnalyzedExercise, CORE_URL, CUMULATIVE_CHANNELS, type ChannelStats, type Credentials, type CumulativeDelta, type Exercise, type ExerciseAnalytics, type Facility, FileSessionStore, type HrSession, type HrZone, type Lap, type ListSessionsOptions, MemorySessionStore, type MywellnessErrorEntry, type Property, type PropertyMap, type RawPayload, SERVICES_URL, SETPOINT_CHANNELS, type Segment, type SegmentBy, type SegmentMode, type SegmentOptions, type ServiceOptions, type Session, type SessionStore, type SessionSummary, type SetData, Technogym, TechnogymApiError, TechnogymAuthError, TechnogymClient, type TechnogymClientOptions, TechnogymConnectionError, TechnogymError, TechnogymHttpError, TechnogymNotFoundError, TechnogymRateLimitError, type UserInfo, type WorkloadRange, type WorkoutSession, channelStats, downsample, isAuthErrorEntry, parseExercise, parseExerciseAnalytics, parseHrSession, parseProperties, parseSessionSummary, parseUser, parseWorkoutSession, segmentAnalytics, windowSamples };