@fytkit/sdk 3.2.0 → 3.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/CHANGELOG.md +24 -0
- package/README.md +16 -3
- package/dist/index.d.ts +200 -32
- package/dist/index.js +76 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ All notable changes to `@fytkit/sdk` are documented in this file.
|
|
|
4
4
|
Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
Versioning: [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [3.4.0](https://github.com/cbarlow1993/fytkit-web/compare/@fytkit/sdk@3.3.0...@fytkit/sdk@3.4.0) (2026-10-04)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* **ai-chat:** trainer AI assistant with prepaid credits — rebased on main, review fixes, launch config ([#422](https://github.com/cbarlow1993/fytkit-web/issues/422)) ([f33f4dd](https://github.com/cbarlow1993/fytkit-web/commit/f33f4ddf4a77be117db1b2de6f4274760e8d4391))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* follow-ups from the [#442](https://github.com/cbarlow1993/fytkit-web/issues/442) review ([#443](https://github.com/cbarlow1993/fytkit-web/issues/443)–[#446](https://github.com/cbarlow1993/fytkit-web/issues/446)) ([#447](https://github.com/cbarlow1993/fytkit-web/issues/447)) ([7cccd36](https://github.com/cbarlow1993/fytkit-web/commit/7cccd3679b8b29428dbcebec5b68f47d9edb8487))
|
|
18
|
+
|
|
19
|
+
## [3.3.0](https://github.com/cbarlow1993/fytkit-web/compare/@fytkit/sdk@3.2.0...@fytkit/sdk@3.3.0) (2026-10-01)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* **public-api:** list/search/filter exercises; open the workout, exercise and program catalogues ([#407](https://github.com/cbarlow1993/fytkit-web/issues/407)) ([c1bb93f](https://github.com/cbarlow1993/fytkit-web/commit/c1bb93f44e5773ea15cb7b4300d003ee4f531323))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Bug Fixes
|
|
28
|
+
|
|
29
|
+
* public API and SDK fixes from the Fitness with Rachael pre-production review ([#442](https://github.com/cbarlow1993/fytkit-web/issues/442)) ([49e8038](https://github.com/cbarlow1993/fytkit-web/commit/49e80381f4717cf40d14ab0079b9bf2ce22d4f26))
|
|
30
|
+
|
|
7
31
|
## [3.2.0](https://github.com/cbarlow1993/fytkit-web/compare/@fytkit/sdk@3.1.0...@fytkit/sdk@3.2.0) (2026-09-05)
|
|
8
32
|
|
|
9
33
|
|
package/README.md
CHANGED
|
@@ -128,8 +128,21 @@ await fytkit.auth.changeEmail({ newEmail });
|
|
|
128
128
|
const sessions = await fytkit.auth.listSessions();
|
|
129
129
|
await fytkit.auth.revokeSession({ revokeId: sessions.find((s) => !s.isCurrent)!.revokeId });
|
|
130
130
|
await fytkit.auth.revokeOtherSessions(); // "sign out of all other devices"
|
|
131
|
+
|
|
132
|
+
// Permanently close the account (re-verifies email + password). On success the
|
|
133
|
+
// SDK clears the session and emits signed_out.
|
|
134
|
+
await fytkit.auth.closeAccount({ confirmEmail, password });
|
|
131
135
|
```
|
|
132
136
|
|
|
137
|
+
A wrong password or one-time code is a `{ kind: "server", status: 400 }` error
|
|
138
|
+
with `code: "invalid_password"` / `"invalid_code"`; a change that needs a
|
|
139
|
+
fresh sign-in is `{ kind: "forbidden", code: "reauth_required" }`. None of
|
|
140
|
+
these sign the trainee out — only a 401 on an authenticated call means the
|
|
141
|
+
session is gone. A wrong password on `signIn` (for example, signing in again
|
|
142
|
+
after `reauth_required`) is a plain `auth_required` and leaves the current
|
|
143
|
+
session alone. Enrolling twice in a program is `{ kind: "conflict" }` with the
|
|
144
|
+
existing `enrollmentId`.
|
|
145
|
+
|
|
133
146
|
## Notification preferences
|
|
134
147
|
|
|
135
148
|
Per-member toggles stored server-side (defaults apply until first write):
|
|
@@ -144,17 +157,17 @@ await fytkit.preferences.updateNotifications({ streakAlerts: false }); // partia
|
|
|
144
157
|
- `auth.signUp` / `signIn` / `signOut` / `getSession` / `onAuthStateChange`
|
|
145
158
|
- `auth.requestPasswordReset` / `confirmPasswordReset` / `confirmEmail`
|
|
146
159
|
- Two-factor: `auth.verifyTotp` / `verifyBackupCode` / `requestEmailOtp` / `verifyEmailOtp` (sign-in), `enableTotp` / `verifyTotpSetup` / `disableTwoFactor` / `regenerateBackupCodes` / `getSecurityStatus` (management)
|
|
147
|
-
- Account security: `auth.changePassword` / `changeEmail` / `listSessions` / `revokeSession` / `revokeOtherSessions`
|
|
160
|
+
- Account security: `auth.changePassword` / `changeEmail` / `listSessions` / `revokeSession` / `revokeOtherSessions` / `closeAccount`
|
|
148
161
|
- Data namespaces: `workouts`, `programs`, `recipes`, `nutrition`, `nutritionLog`, `profile`, `preferences`, `articles`, `enrollments`, `sessionLogs`, `progression`, `progress`, `bodyMetrics`, `media`, `appConfig`, `subscription`
|
|
149
162
|
- Workout tracking: program session logs and ad-hoc workout logs (`sessionLogs.create` with `workoutId`), set logging, per-workout history (`sessionLogs.list({ workoutId })`)
|
|
150
163
|
- Progress: `progress.getSummary` / `getAdherence` / `getStreak` (stored streaks), `progression.getPrSummary` / `listPrHistory` (stored PR history) / `getHistory`, `bodyMetrics.list` / `record` / `delete`
|
|
151
164
|
- Automatic header injection (`X-Fytkit-Client-Id`, `Authorization: Bearer …`)
|
|
152
|
-
- Proactive refresh ~60 s before token expiry + reactive 401 retry
|
|
165
|
+
- Proactive refresh ~60 s before token expiry (retried with backoff when offline) + reactive 401 retry; only a server-rejected refresh signs out
|
|
153
166
|
- Pluggable token storage (`localStorage` default, in-memory + cookie for SSR)
|
|
154
167
|
|
|
155
168
|
## Storage trade-off
|
|
156
169
|
|
|
157
|
-
Access tokens land in `localStorage` by default, namespaced by `clientId`. Any script running on your origin can read them — ship a strict CSP and treat the SDK like any other auth-bearing dependency.
|
|
170
|
+
Access tokens land in `localStorage` by default, namespaced by `clientId`. Any script running on your origin can read them — ship a strict CSP and treat the SDK like any other auth-bearing dependency. A token is a server session that lasts up to 7 days; signing out, closing the account or revoking the developer app ends it server-side.
|
|
158
171
|
|
|
159
172
|
Use `createMemoryStorageAdapter()` for SSR + tests.
|
|
160
173
|
|
package/dist/index.d.ts
CHANGED
|
@@ -151,6 +151,15 @@ export declare interface FytkitClient {
|
|
|
151
151
|
revokeId: string;
|
|
152
152
|
}) => Promise<void>;
|
|
153
153
|
revokeOtherSessions: () => Promise<void>;
|
|
154
|
+
/** Permanently closes the member's account (cancels their subscriptions,
|
|
155
|
+
* anonymises their data, deletes the login) after re-verifying the email
|
|
156
|
+
* and password. On success the SDK clears its session and emits
|
|
157
|
+
* `signed_out`. A wrong password rejects with `{ kind: "server", status:
|
|
158
|
+
* 400, code: "invalid_password" }` and leaves the session signed in. */
|
|
159
|
+
closeAccount: (input: {
|
|
160
|
+
confirmEmail: string;
|
|
161
|
+
password: string;
|
|
162
|
+
}) => Promise<void>;
|
|
154
163
|
};
|
|
155
164
|
readonly workouts: PublicApi["workout"];
|
|
156
165
|
readonly exercises: PublicApi["exercise"];
|
|
@@ -189,12 +198,14 @@ export declare type FytkitError = {
|
|
|
189
198
|
} | {
|
|
190
199
|
readonly kind: "forbidden";
|
|
191
200
|
readonly reason: string;
|
|
201
|
+
readonly code?: FytkitErrorCode;
|
|
192
202
|
} | {
|
|
193
203
|
readonly kind: "payment_required";
|
|
194
204
|
readonly message: string;
|
|
195
205
|
} | {
|
|
196
206
|
readonly kind: "conflict";
|
|
197
207
|
readonly message: string;
|
|
208
|
+
readonly enrollmentId?: string;
|
|
198
209
|
} | {
|
|
199
210
|
readonly kind: "not_found";
|
|
200
211
|
} | {
|
|
@@ -207,10 +218,29 @@ export declare type FytkitError = {
|
|
|
207
218
|
readonly kind: "server";
|
|
208
219
|
readonly status: number;
|
|
209
220
|
readonly message?: string;
|
|
221
|
+
readonly code?: FytkitErrorCode;
|
|
210
222
|
} | {
|
|
211
223
|
readonly kind: "network";
|
|
212
224
|
};
|
|
213
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Machine-readable reasons the server attaches to some errors (as `code` on
|
|
228
|
+
* the `forbidden` and `server` kinds). Branch on these instead of matching
|
|
229
|
+
* `message`/`reason` text. The union is open — the server may add codes, so
|
|
230
|
+
* always keep a fallback branch.
|
|
231
|
+
*
|
|
232
|
+
* - `"reauth_required"` (`forbidden`, 403) — the session is valid but too old
|
|
233
|
+
* for this change (e.g. `changeEmail` more than 15 minutes after sign-in).
|
|
234
|
+
* Ask the member to sign in again; do NOT treat it as signed out.
|
|
235
|
+
* - `"invalid_password"` (`server`, 400) — the re-typed password was wrong
|
|
236
|
+
* (`changePassword`, `closeAccount`, `enableTotp`, `disableTwoFactor`,
|
|
237
|
+
* `regenerateBackupCodes`). The session is untouched.
|
|
238
|
+
* - `"invalid_code"` (`server`, 400) — a TOTP, backup or email one-time code
|
|
239
|
+
* was wrong (`verifyTotp`, `verifyBackupCode`, `verifyEmailOtp`,
|
|
240
|
+
* `verifyTotpSetup`).
|
|
241
|
+
*/
|
|
242
|
+
export declare type FytkitErrorCode = "reauth_required" | "invalid_password" | "invalid_code" | (string & {});
|
|
243
|
+
|
|
214
244
|
export declare type PublicApi = RouterClient<PublicRouter>;
|
|
215
245
|
|
|
216
246
|
declare type PublicRouter = typeof publicRouter;
|
|
@@ -809,6 +839,10 @@ declare const publicRouter: {
|
|
|
809
839
|
intermediate: "intermediate";
|
|
810
840
|
advanced: "advanced";
|
|
811
841
|
}>>;
|
|
842
|
+
accessLevel: ZodOptional<ZodEnum< {
|
|
843
|
+
free: "free";
|
|
844
|
+
premium: "premium";
|
|
845
|
+
}>>;
|
|
812
846
|
equipmentAvailable: ZodOptional<ZodPipe<ZodUnion<readonly [ZodArray<ZodString>, ZodString]>, ZodTransform<string[], string | string[]>>>;
|
|
813
847
|
locations: ZodOptional<ZodPipe<ZodUnion<readonly [ZodArray<ZodString>, ZodString]>, ZodTransform<("home" | "gym" | "outdoors")[], string | string[]>>>;
|
|
814
848
|
favouritesOnly: ZodOptional<ZodUnion<readonly [ZodBoolean, ZodCodec<ZodString, ZodBoolean>]>>;
|
|
@@ -822,9 +856,10 @@ declare const publicRouter: {
|
|
|
822
856
|
free: "free";
|
|
823
857
|
premium: "premium";
|
|
824
858
|
}>;
|
|
859
|
+
canAccess: ZodBoolean;
|
|
825
860
|
status: ZodEnum< {
|
|
826
|
-
draft: "draft";
|
|
827
861
|
published: "published";
|
|
862
|
+
draft: "draft";
|
|
828
863
|
archived: "archived";
|
|
829
864
|
}>;
|
|
830
865
|
durationMinutes: ZodNullable<ZodNumber>;
|
|
@@ -875,9 +910,10 @@ declare const publicRouter: {
|
|
|
875
910
|
free: "free";
|
|
876
911
|
premium: "premium";
|
|
877
912
|
}>;
|
|
913
|
+
canAccess: ZodBoolean;
|
|
878
914
|
status: ZodEnum< {
|
|
879
|
-
draft: "draft";
|
|
880
915
|
published: "published";
|
|
916
|
+
draft: "draft";
|
|
881
917
|
archived: "archived";
|
|
882
918
|
}>;
|
|
883
919
|
durationMinutes: ZodNullable<ZodNumber>;
|
|
@@ -985,9 +1021,10 @@ declare const publicRouter: {
|
|
|
985
1021
|
free: "free";
|
|
986
1022
|
premium: "premium";
|
|
987
1023
|
}>;
|
|
1024
|
+
canAccess: ZodBoolean;
|
|
988
1025
|
status: ZodEnum< {
|
|
989
|
-
draft: "draft";
|
|
990
1026
|
published: "published";
|
|
1027
|
+
draft: "draft";
|
|
991
1028
|
archived: "archived";
|
|
992
1029
|
}>;
|
|
993
1030
|
durationMinutes: ZodNullable<ZodNumber>;
|
|
@@ -1030,9 +1067,10 @@ declare const publicRouter: {
|
|
|
1030
1067
|
free: "free";
|
|
1031
1068
|
premium: "premium";
|
|
1032
1069
|
}>;
|
|
1070
|
+
canAccess: ZodBoolean;
|
|
1033
1071
|
status: ZodEnum< {
|
|
1034
|
-
draft: "draft";
|
|
1035
1072
|
published: "published";
|
|
1073
|
+
draft: "draft";
|
|
1036
1074
|
archived: "archived";
|
|
1037
1075
|
}>;
|
|
1038
1076
|
durationMinutes: ZodNullable<ZodNumber>;
|
|
@@ -1122,9 +1160,10 @@ declare const publicRouter: {
|
|
|
1122
1160
|
free: "free";
|
|
1123
1161
|
premium: "premium";
|
|
1124
1162
|
}>;
|
|
1163
|
+
canAccess: ZodBoolean;
|
|
1125
1164
|
status: ZodEnum< {
|
|
1126
|
-
draft: "draft";
|
|
1127
1165
|
published: "published";
|
|
1166
|
+
draft: "draft";
|
|
1128
1167
|
archived: "archived";
|
|
1129
1168
|
}>;
|
|
1130
1169
|
publishedAt: ZodNullable<ZodDate>;
|
|
@@ -1164,9 +1203,10 @@ declare const publicRouter: {
|
|
|
1164
1203
|
free: "free";
|
|
1165
1204
|
premium: "premium";
|
|
1166
1205
|
}>;
|
|
1206
|
+
canAccess: ZodBoolean;
|
|
1167
1207
|
status: ZodEnum< {
|
|
1168
|
-
draft: "draft";
|
|
1169
1208
|
published: "published";
|
|
1209
|
+
draft: "draft";
|
|
1170
1210
|
archived: "archived";
|
|
1171
1211
|
}>;
|
|
1172
1212
|
publishedAt: ZodNullable<ZodDate>;
|
|
@@ -1192,8 +1232,8 @@ declare const publicRouter: {
|
|
|
1192
1232
|
dayIndex: ZodNumber;
|
|
1193
1233
|
kind: ZodEnum< {
|
|
1194
1234
|
custom: "custom";
|
|
1195
|
-
note: "note";
|
|
1196
1235
|
recipe: "recipe";
|
|
1236
|
+
note: "note";
|
|
1197
1237
|
}>;
|
|
1198
1238
|
recipeId: ZodNullable<ZodString>;
|
|
1199
1239
|
servings: ZodNumber;
|
|
@@ -1222,9 +1262,10 @@ declare const publicRouter: {
|
|
|
1222
1262
|
free: "free";
|
|
1223
1263
|
premium: "premium";
|
|
1224
1264
|
}>;
|
|
1265
|
+
canAccess: ZodBoolean;
|
|
1225
1266
|
status: ZodEnum< {
|
|
1226
|
-
draft: "draft";
|
|
1227
1267
|
published: "published";
|
|
1268
|
+
draft: "draft";
|
|
1228
1269
|
archived: "archived";
|
|
1229
1270
|
}>;
|
|
1230
1271
|
publishedAt: ZodNullable<ZodDate>;
|
|
@@ -1417,8 +1458,8 @@ declare const publicRouter: {
|
|
|
1417
1458
|
email: ZodString;
|
|
1418
1459
|
avatarUrl: ZodNullable<ZodString>;
|
|
1419
1460
|
weightUnit: ZodEnum< {
|
|
1420
|
-
kg: "kg";
|
|
1421
1461
|
lb: "lb";
|
|
1462
|
+
kg: "kg";
|
|
1422
1463
|
}>;
|
|
1423
1464
|
timezone: ZodNullable<ZodString>;
|
|
1424
1465
|
fitnessGoal: ZodNullable<ZodEnum< {
|
|
@@ -1452,8 +1493,8 @@ declare const publicRouter: {
|
|
|
1452
1493
|
avatarUrl: ZodOptional<ZodNullable<ZodString>>;
|
|
1453
1494
|
timezone: ZodOptional<ZodNullable<ZodString>>;
|
|
1454
1495
|
weightUnit: ZodOptional<ZodEnum< {
|
|
1455
|
-
kg: "kg";
|
|
1456
1496
|
lb: "lb";
|
|
1497
|
+
kg: "kg";
|
|
1457
1498
|
}>>;
|
|
1458
1499
|
fitnessGoal: ZodOptional<ZodNullable<ZodEnum< {
|
|
1459
1500
|
lose_fat: "lose_fat";
|
|
@@ -1471,8 +1512,8 @@ declare const publicRouter: {
|
|
|
1471
1512
|
email: ZodString;
|
|
1472
1513
|
avatarUrl: ZodNullable<ZodString>;
|
|
1473
1514
|
weightUnit: ZodEnum< {
|
|
1474
|
-
kg: "kg";
|
|
1475
1515
|
lb: "lb";
|
|
1516
|
+
kg: "kg";
|
|
1476
1517
|
}>;
|
|
1477
1518
|
timezone: ZodNullable<ZodString>;
|
|
1478
1519
|
fitnessGoal: ZodNullable<ZodEnum< {
|
|
@@ -1559,9 +1600,10 @@ declare const publicRouter: {
|
|
|
1559
1600
|
free: "free";
|
|
1560
1601
|
premium: "premium";
|
|
1561
1602
|
}>;
|
|
1603
|
+
canAccess: ZodBoolean;
|
|
1562
1604
|
status: ZodEnum< {
|
|
1563
|
-
draft: "draft";
|
|
1564
1605
|
published: "published";
|
|
1606
|
+
draft: "draft";
|
|
1565
1607
|
archived: "archived";
|
|
1566
1608
|
}>;
|
|
1567
1609
|
publishedAt: ZodNullable<ZodDate>;
|
|
@@ -1593,9 +1635,10 @@ declare const publicRouter: {
|
|
|
1593
1635
|
free: "free";
|
|
1594
1636
|
premium: "premium";
|
|
1595
1637
|
}>;
|
|
1638
|
+
canAccess: ZodBoolean;
|
|
1596
1639
|
status: ZodEnum< {
|
|
1597
|
-
draft: "draft";
|
|
1598
1640
|
published: "published";
|
|
1641
|
+
draft: "draft";
|
|
1599
1642
|
archived: "archived";
|
|
1600
1643
|
}>;
|
|
1601
1644
|
publishedAt: ZodNullable<ZodDate>;
|
|
@@ -1644,9 +1687,10 @@ declare const publicRouter: {
|
|
|
1644
1687
|
free: "free";
|
|
1645
1688
|
premium: "premium";
|
|
1646
1689
|
}>;
|
|
1690
|
+
canAccess: ZodBoolean;
|
|
1647
1691
|
status: ZodEnum< {
|
|
1648
|
-
draft: "draft";
|
|
1649
1692
|
published: "published";
|
|
1693
|
+
draft: "draft";
|
|
1650
1694
|
archived: "archived";
|
|
1651
1695
|
}>;
|
|
1652
1696
|
publishedAt: ZodNullable<ZodDate>;
|
|
@@ -1690,9 +1734,10 @@ declare const publicRouter: {
|
|
|
1690
1734
|
free: "free";
|
|
1691
1735
|
premium: "premium";
|
|
1692
1736
|
}>;
|
|
1737
|
+
canAccess: ZodBoolean;
|
|
1693
1738
|
status: ZodEnum< {
|
|
1694
|
-
draft: "draft";
|
|
1695
1739
|
published: "published";
|
|
1740
|
+
draft: "draft";
|
|
1696
1741
|
archived: "archived";
|
|
1697
1742
|
}>;
|
|
1698
1743
|
publishedAt: ZodNullable<ZodDate>;
|
|
@@ -1721,6 +1766,72 @@ declare const publicRouter: {
|
|
|
1721
1766
|
}, $strip>, Record<never, never>, Record<never, never>>;
|
|
1722
1767
|
};
|
|
1723
1768
|
exercise: {
|
|
1769
|
+
list: DecoratedProcedure<MergedInitialContext< {
|
|
1770
|
+
headers: Headers;
|
|
1771
|
+
} & Record<never, never>, {
|
|
1772
|
+
headers: Headers;
|
|
1773
|
+
} & Record<never, never>, {
|
|
1774
|
+
headers: Headers;
|
|
1775
|
+
}>, MergedCurrentContext< {
|
|
1776
|
+
headers: Headers;
|
|
1777
|
+
}, ApiKeyContext>, ZodObject< {
|
|
1778
|
+
cursor: ZodOptional<ZodString>;
|
|
1779
|
+
limit: ZodDefault<ZodCoercedNumber<unknown>>;
|
|
1780
|
+
q: ZodOptional<ZodString>;
|
|
1781
|
+
type: ZodOptional<ZodEnum< {
|
|
1782
|
+
compound: "compound";
|
|
1783
|
+
isolation: "isolation";
|
|
1784
|
+
cardio: "cardio";
|
|
1785
|
+
mobility: "mobility";
|
|
1786
|
+
}>>;
|
|
1787
|
+
difficulty: ZodOptional<ZodEnum< {
|
|
1788
|
+
beginner: "beginner";
|
|
1789
|
+
intermediate: "intermediate";
|
|
1790
|
+
advanced: "advanced";
|
|
1791
|
+
}>>;
|
|
1792
|
+
accessLevel: ZodOptional<ZodEnum< {
|
|
1793
|
+
free: "free";
|
|
1794
|
+
premium: "premium";
|
|
1795
|
+
}>>;
|
|
1796
|
+
muscleGroups: ZodOptional<ZodPipe<ZodUnion<readonly [ZodArray<ZodString>, ZodString]>, ZodTransform<string[], string | string[]>>>;
|
|
1797
|
+
equipmentAvailable: ZodOptional<ZodPipe<ZodUnion<readonly [ZodArray<ZodString>, ZodString]>, ZodTransform<string[], string | string[]>>>;
|
|
1798
|
+
hasVideo: ZodOptional<ZodUnion<readonly [ZodBoolean, ZodCodec<ZodString, ZodBoolean>]>>;
|
|
1799
|
+
}, $strip>, ZodObject< {
|
|
1800
|
+
data: ZodArray<ZodObject< {
|
|
1801
|
+
id: ZodString;
|
|
1802
|
+
trainerId: ZodString;
|
|
1803
|
+
name: ZodString;
|
|
1804
|
+
description: ZodString;
|
|
1805
|
+
cue: ZodString;
|
|
1806
|
+
type: ZodEnum< {
|
|
1807
|
+
compound: "compound";
|
|
1808
|
+
isolation: "isolation";
|
|
1809
|
+
cardio: "cardio";
|
|
1810
|
+
mobility: "mobility";
|
|
1811
|
+
}>;
|
|
1812
|
+
accessLevel: ZodEnum< {
|
|
1813
|
+
free: "free";
|
|
1814
|
+
premium: "premium";
|
|
1815
|
+
}>;
|
|
1816
|
+
canAccess: ZodBoolean;
|
|
1817
|
+
difficulty: ZodEnum< {
|
|
1818
|
+
beginner: "beginner";
|
|
1819
|
+
intermediate: "intermediate";
|
|
1820
|
+
advanced: "advanced";
|
|
1821
|
+
}>;
|
|
1822
|
+
muscleGroups: ZodArray<ZodString>;
|
|
1823
|
+
equipment: ZodArray<ZodString>;
|
|
1824
|
+
headerImageId: ZodNullable<ZodString>;
|
|
1825
|
+
headerImageUrl: ZodNullable<ZodString>;
|
|
1826
|
+
videoAssetId: ZodNullable<ZodString>;
|
|
1827
|
+
videoAutoLoop: ZodBoolean;
|
|
1828
|
+
createdAt: ZodDate;
|
|
1829
|
+
}, $strip>>;
|
|
1830
|
+
meta: ZodObject< {
|
|
1831
|
+
nextCursor: ZodNullable<ZodString>;
|
|
1832
|
+
limit: ZodNumber;
|
|
1833
|
+
}, $strip>;
|
|
1834
|
+
}, $strip>, Record<never, never>, Record<never, never>>;
|
|
1724
1835
|
get: DecoratedProcedure<MergedInitialContext< {
|
|
1725
1836
|
headers: Headers;
|
|
1726
1837
|
} & Record<never, never>, {
|
|
@@ -1748,6 +1859,7 @@ declare const publicRouter: {
|
|
|
1748
1859
|
free: "free";
|
|
1749
1860
|
premium: "premium";
|
|
1750
1861
|
}>;
|
|
1862
|
+
canAccess: ZodBoolean;
|
|
1751
1863
|
difficulty: ZodEnum< {
|
|
1752
1864
|
beginner: "beginner";
|
|
1753
1865
|
intermediate: "intermediate";
|
|
@@ -1759,6 +1871,7 @@ declare const publicRouter: {
|
|
|
1759
1871
|
headerImageUrl: ZodNullable<ZodString>;
|
|
1760
1872
|
videoAssetId: ZodNullable<ZodString>;
|
|
1761
1873
|
videoAutoLoop: ZodBoolean;
|
|
1874
|
+
createdAt: ZodDate;
|
|
1762
1875
|
instructions: ZodArray<ZodObject< {
|
|
1763
1876
|
id: ZodString;
|
|
1764
1877
|
order: ZodNumber;
|
|
@@ -1811,6 +1924,10 @@ declare const publicRouter: {
|
|
|
1811
1924
|
intermediate: "intermediate";
|
|
1812
1925
|
advanced: "advanced";
|
|
1813
1926
|
}>>;
|
|
1927
|
+
accessLevel: ZodOptional<ZodEnum< {
|
|
1928
|
+
free: "free";
|
|
1929
|
+
premium: "premium";
|
|
1930
|
+
}>>;
|
|
1814
1931
|
goalId: ZodOptional<ZodString>;
|
|
1815
1932
|
equipmentAvailable: ZodOptional<ZodPipe<ZodUnion<readonly [ZodArray<ZodString>, ZodString]>, ZodTransform<string[], string | string[]>>>;
|
|
1816
1933
|
locations: ZodOptional<ZodPipe<ZodUnion<readonly [ZodArray<ZodString>, ZodString]>, ZodTransform<("home" | "gym" | "outdoors")[], string | string[]>>>;
|
|
@@ -1825,9 +1942,10 @@ declare const publicRouter: {
|
|
|
1825
1942
|
free: "free";
|
|
1826
1943
|
premium: "premium";
|
|
1827
1944
|
}>;
|
|
1945
|
+
canAccess: ZodBoolean;
|
|
1828
1946
|
status: ZodEnum< {
|
|
1829
|
-
draft: "draft";
|
|
1830
1947
|
published: "published";
|
|
1948
|
+
draft: "draft";
|
|
1831
1949
|
archived: "archived";
|
|
1832
1950
|
}>;
|
|
1833
1951
|
difficulty: ZodNullable<ZodEnum< {
|
|
@@ -1845,6 +1963,8 @@ declare const publicRouter: {
|
|
|
1845
1963
|
coverImageUrl: ZodNullable<ZodString>;
|
|
1846
1964
|
publishedAt: ZodNullable<ZodString>;
|
|
1847
1965
|
createdAt: ZodString;
|
|
1966
|
+
sessionsPerWeek: ZodNullable<ZodNumber>;
|
|
1967
|
+
weekCount: ZodNumber;
|
|
1848
1968
|
}, $strip>>;
|
|
1849
1969
|
meta: ZodObject< {
|
|
1850
1970
|
nextCursor: ZodNullable<ZodString>;
|
|
@@ -1871,9 +1991,10 @@ declare const publicRouter: {
|
|
|
1871
1991
|
free: "free";
|
|
1872
1992
|
premium: "premium";
|
|
1873
1993
|
}>;
|
|
1994
|
+
canAccess: ZodBoolean;
|
|
1874
1995
|
status: ZodEnum< {
|
|
1875
|
-
draft: "draft";
|
|
1876
1996
|
published: "published";
|
|
1997
|
+
draft: "draft";
|
|
1877
1998
|
archived: "archived";
|
|
1878
1999
|
}>;
|
|
1879
2000
|
difficulty: ZodNullable<ZodEnum< {
|
|
@@ -1918,12 +2039,12 @@ declare const publicRouter: {
|
|
|
1918
2039
|
weekOrder: ZodNullable<ZodNumber>;
|
|
1919
2040
|
dayOfWeek: ZodEnum< {
|
|
1920
2041
|
mon: "mon";
|
|
2042
|
+
sun: "sun";
|
|
1921
2043
|
tue: "tue";
|
|
1922
2044
|
wed: "wed";
|
|
1923
2045
|
thu: "thu";
|
|
1924
2046
|
fri: "fri";
|
|
1925
2047
|
sat: "sat";
|
|
1926
|
-
sun: "sun";
|
|
1927
2048
|
}>;
|
|
1928
2049
|
workoutId: ZodString;
|
|
1929
2050
|
order: ZodNumber;
|
|
@@ -1978,12 +2099,12 @@ declare const publicRouter: {
|
|
|
1978
2099
|
weekOrder: ZodNullable<ZodNumber>;
|
|
1979
2100
|
dayOfWeek: ZodEnum< {
|
|
1980
2101
|
mon: "mon";
|
|
2102
|
+
sun: "sun";
|
|
1981
2103
|
tue: "tue";
|
|
1982
2104
|
wed: "wed";
|
|
1983
2105
|
thu: "thu";
|
|
1984
2106
|
fri: "fri";
|
|
1985
2107
|
sat: "sat";
|
|
1986
|
-
sun: "sun";
|
|
1987
2108
|
}>;
|
|
1988
2109
|
workoutId: ZodString;
|
|
1989
2110
|
order: ZodNumber;
|
|
@@ -2051,12 +2172,12 @@ declare const publicRouter: {
|
|
|
2051
2172
|
weekOrder: ZodNullable<ZodNumber>;
|
|
2052
2173
|
dayOfWeek: ZodEnum< {
|
|
2053
2174
|
mon: "mon";
|
|
2175
|
+
sun: "sun";
|
|
2054
2176
|
tue: "tue";
|
|
2055
2177
|
wed: "wed";
|
|
2056
2178
|
thu: "thu";
|
|
2057
2179
|
fri: "fri";
|
|
2058
2180
|
sat: "sat";
|
|
2059
|
-
sun: "sun";
|
|
2060
2181
|
}>;
|
|
2061
2182
|
workoutId: ZodString;
|
|
2062
2183
|
order: ZodNumber;
|
|
@@ -2146,9 +2267,9 @@ declare const publicRouter: {
|
|
|
2146
2267
|
self: "self";
|
|
2147
2268
|
}>;
|
|
2148
2269
|
status: ZodEnum< {
|
|
2270
|
+
completed: "completed";
|
|
2149
2271
|
active: "active";
|
|
2150
2272
|
removed: "removed";
|
|
2151
|
-
completed: "completed";
|
|
2152
2273
|
}>;
|
|
2153
2274
|
enrolledAt: ZodString;
|
|
2154
2275
|
removedAt: ZodNullable<ZodString>;
|
|
@@ -2162,9 +2283,10 @@ declare const publicRouter: {
|
|
|
2162
2283
|
free: "free";
|
|
2163
2284
|
premium: "premium";
|
|
2164
2285
|
}>;
|
|
2286
|
+
canAccess: ZodBoolean;
|
|
2165
2287
|
status: ZodEnum< {
|
|
2166
|
-
draft: "draft";
|
|
2167
2288
|
published: "published";
|
|
2289
|
+
draft: "draft";
|
|
2168
2290
|
archived: "archived";
|
|
2169
2291
|
}>;
|
|
2170
2292
|
difficulty: ZodNullable<ZodEnum< {
|
|
@@ -2210,7 +2332,7 @@ declare const publicRouter: {
|
|
|
2210
2332
|
id: string;
|
|
2211
2333
|
programId: string;
|
|
2212
2334
|
source: "trainer" | "self";
|
|
2213
|
-
status: "
|
|
2335
|
+
status: "completed" | "active" | "removed";
|
|
2214
2336
|
enrolledAt: string;
|
|
2215
2337
|
};
|
|
2216
2338
|
}, {
|
|
@@ -2218,7 +2340,7 @@ declare const publicRouter: {
|
|
|
2218
2340
|
id: string;
|
|
2219
2341
|
programId: string;
|
|
2220
2342
|
source: "trainer" | "self";
|
|
2221
|
-
status: "
|
|
2343
|
+
status: "completed" | "active" | "removed";
|
|
2222
2344
|
enrolledAt: string;
|
|
2223
2345
|
};
|
|
2224
2346
|
}>, Record<never, never>, Record<never, never>>;
|
|
@@ -2259,16 +2381,16 @@ declare const publicRouter: {
|
|
|
2259
2381
|
entityType: ZodOptional<ZodEnum< {
|
|
2260
2382
|
workout: "workout";
|
|
2261
2383
|
program: "program";
|
|
2262
|
-
nutrition_plan: "nutrition_plan";
|
|
2263
2384
|
recipe: "recipe";
|
|
2385
|
+
nutrition_plan: "nutrition_plan";
|
|
2264
2386
|
}>>;
|
|
2265
2387
|
}, $strip>, ZodObject< {
|
|
2266
2388
|
data: ZodArray<ZodObject< {
|
|
2267
2389
|
entityType: ZodEnum< {
|
|
2268
2390
|
workout: "workout";
|
|
2269
2391
|
program: "program";
|
|
2270
|
-
nutrition_plan: "nutrition_plan";
|
|
2271
2392
|
recipe: "recipe";
|
|
2393
|
+
nutrition_plan: "nutrition_plan";
|
|
2272
2394
|
}>;
|
|
2273
2395
|
entityId: ZodString;
|
|
2274
2396
|
favouritedAt: ZodString;
|
|
@@ -2290,8 +2412,8 @@ declare const publicRouter: {
|
|
|
2290
2412
|
entityType: ZodEnum< {
|
|
2291
2413
|
workout: "workout";
|
|
2292
2414
|
program: "program";
|
|
2293
|
-
nutrition_plan: "nutrition_plan";
|
|
2294
2415
|
recipe: "recipe";
|
|
2416
|
+
nutrition_plan: "nutrition_plan";
|
|
2295
2417
|
}>;
|
|
2296
2418
|
entityId: ZodString;
|
|
2297
2419
|
}, $strip>, ZodObject< {
|
|
@@ -2299,8 +2421,8 @@ declare const publicRouter: {
|
|
|
2299
2421
|
entityType: ZodEnum< {
|
|
2300
2422
|
workout: "workout";
|
|
2301
2423
|
program: "program";
|
|
2302
|
-
nutrition_plan: "nutrition_plan";
|
|
2303
2424
|
recipe: "recipe";
|
|
2425
|
+
nutrition_plan: "nutrition_plan";
|
|
2304
2426
|
}>;
|
|
2305
2427
|
entityId: ZodString;
|
|
2306
2428
|
favouritedAt: ZodString;
|
|
@@ -2318,8 +2440,8 @@ declare const publicRouter: {
|
|
|
2318
2440
|
entityType: ZodEnum< {
|
|
2319
2441
|
workout: "workout";
|
|
2320
2442
|
program: "program";
|
|
2321
|
-
nutrition_plan: "nutrition_plan";
|
|
2322
2443
|
recipe: "recipe";
|
|
2444
|
+
nutrition_plan: "nutrition_plan";
|
|
2323
2445
|
}>;
|
|
2324
2446
|
entityId: ZodString;
|
|
2325
2447
|
}, $strip>, ZodObject< {
|
|
@@ -2327,8 +2449,8 @@ declare const publicRouter: {
|
|
|
2327
2449
|
entityType: ZodEnum< {
|
|
2328
2450
|
workout: "workout";
|
|
2329
2451
|
program: "program";
|
|
2330
|
-
nutrition_plan: "nutrition_plan";
|
|
2331
2452
|
recipe: "recipe";
|
|
2453
|
+
nutrition_plan: "nutrition_plan";
|
|
2332
2454
|
}>;
|
|
2333
2455
|
entityId: ZodString;
|
|
2334
2456
|
deleted: ZodLiteral<true>;
|
|
@@ -2835,10 +2957,10 @@ declare const publicRouter: {
|
|
|
2835
2957
|
adaptiveIconUrl: ZodNullable<ZodString>;
|
|
2836
2958
|
faviconUrl: ZodNullable<ZodString>;
|
|
2837
2959
|
buildStatus: ZodEnum< {
|
|
2838
|
-
published: "published";
|
|
2839
2960
|
idle: "idle";
|
|
2840
|
-
building: "building";
|
|
2841
2961
|
failed: "failed";
|
|
2962
|
+
building: "building";
|
|
2963
|
+
published: "published";
|
|
2842
2964
|
}>;
|
|
2843
2965
|
buildVersion: ZodNullable<ZodString>;
|
|
2844
2966
|
helpUrl: ZodNullable<ZodString>;
|
|
@@ -2924,6 +3046,7 @@ declare const publicRouter: {
|
|
|
2924
3046
|
startedAt: ZodNullable<ZodDate>;
|
|
2925
3047
|
expiresAt: ZodNullable<ZodDate>;
|
|
2926
3048
|
}, $strip>>;
|
|
3049
|
+
hasBillingAccount: ZodBoolean;
|
|
2927
3050
|
entitlements: ZodUnion<readonly [ZodLiteral<"all">, ZodArray<ZodEnum< {
|
|
2928
3051
|
premium_workouts: "premium_workouts";
|
|
2929
3052
|
premium_programs: "premium_programs";
|
|
@@ -2953,6 +3076,51 @@ declare const publicRouter: {
|
|
|
2953
3076
|
url: ZodString;
|
|
2954
3077
|
}, $strip>;
|
|
2955
3078
|
}, $strip>, Record<never, never>, Record<never, never>>;
|
|
3079
|
+
confirmCheckout: DecoratedProcedure<MergedInitialContext< {
|
|
3080
|
+
headers: Headers;
|
|
3081
|
+
} & Record<never, never>, {
|
|
3082
|
+
headers: Headers;
|
|
3083
|
+
} & Record<never, never>, {
|
|
3084
|
+
headers: Headers;
|
|
3085
|
+
}>, MergedCurrentContext< {
|
|
3086
|
+
headers: Headers;
|
|
3087
|
+
}, ApiKeyContext>, ZodObject< {
|
|
3088
|
+
sessionId: ZodString;
|
|
3089
|
+
}, $strip>, ZodObject< {
|
|
3090
|
+
data: ZodObject< {
|
|
3091
|
+
hasActiveSubscription: ZodBoolean;
|
|
3092
|
+
subscription: ZodNullable<ZodObject< {
|
|
3093
|
+
plan: ZodEnum< {
|
|
3094
|
+
free: "free";
|
|
3095
|
+
premium: "premium";
|
|
3096
|
+
basic: "basic";
|
|
3097
|
+
}>;
|
|
3098
|
+
planId: ZodNullable<ZodString>;
|
|
3099
|
+
planName: ZodNullable<ZodString>;
|
|
3100
|
+
status: ZodEnum< {
|
|
3101
|
+
active: "active";
|
|
3102
|
+
cancelled: "cancelled";
|
|
3103
|
+
past_due: "past_due";
|
|
3104
|
+
}>;
|
|
3105
|
+
interval: ZodNullable<ZodEnum< {
|
|
3106
|
+
monthly: "monthly";
|
|
3107
|
+
annual: "annual";
|
|
3108
|
+
}>>;
|
|
3109
|
+
priceCents: ZodNullable<ZodNumber>;
|
|
3110
|
+
startedAt: ZodNullable<ZodDate>;
|
|
3111
|
+
expiresAt: ZodNullable<ZodDate>;
|
|
3112
|
+
}, $strip>>;
|
|
3113
|
+
hasBillingAccount: ZodBoolean;
|
|
3114
|
+
entitlements: ZodUnion<readonly [ZodLiteral<"all">, ZodArray<ZodEnum< {
|
|
3115
|
+
premium_workouts: "premium_workouts";
|
|
3116
|
+
premium_programs: "premium_programs";
|
|
3117
|
+
premium_nutrition: "premium_nutrition";
|
|
3118
|
+
premium_recipes: "premium_recipes";
|
|
3119
|
+
premium_articles: "premium_articles";
|
|
3120
|
+
}>>]>;
|
|
3121
|
+
confirmed: ZodBoolean;
|
|
3122
|
+
}, $strip>;
|
|
3123
|
+
}, $strip>, Record<never, never>, Record<never, never>>;
|
|
2956
3124
|
createPortal: DecoratedProcedure<MergedInitialContext< {
|
|
2957
3125
|
headers: Headers;
|
|
2958
3126
|
} & Record<never, never>, {
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,11 @@ function pickRetryAfter(data) {
|
|
|
12
12
|
const raw = data.retryAfterMs;
|
|
13
13
|
return typeof raw === "number" && raw >= 0 ? raw : 0;
|
|
14
14
|
}
|
|
15
|
+
function pickStringField(data, key) {
|
|
16
|
+
if (!data || typeof data !== "object") return {};
|
|
17
|
+
const raw = data[key];
|
|
18
|
+
return typeof raw === "string" && raw.length > 0 ? { [key]: raw } : {};
|
|
19
|
+
}
|
|
15
20
|
function normalizeError(err) {
|
|
16
21
|
if (err instanceof TypeError) return { kind: "network" };
|
|
17
22
|
if (err instanceof ORPCError) {
|
|
@@ -23,11 +28,19 @@ function normalizeError(err) {
|
|
|
23
28
|
case "UNAUTHORIZED":
|
|
24
29
|
return { kind: "auth_required", message };
|
|
25
30
|
case "FORBIDDEN":
|
|
26
|
-
return {
|
|
31
|
+
return {
|
|
32
|
+
kind: "forbidden",
|
|
33
|
+
reason: message || "Forbidden",
|
|
34
|
+
...pickStringField(data, "code")
|
|
35
|
+
};
|
|
27
36
|
case "PAYMENT_REQUIRED":
|
|
28
37
|
return { kind: "payment_required", message: message || "Payment Required" };
|
|
29
38
|
case "CONFLICT":
|
|
30
|
-
return {
|
|
39
|
+
return {
|
|
40
|
+
kind: "conflict",
|
|
41
|
+
message: message || "Conflict",
|
|
42
|
+
...pickStringField(data, "enrollmentId")
|
|
43
|
+
};
|
|
31
44
|
case "NOT_FOUND":
|
|
32
45
|
return { kind: "not_found" };
|
|
33
46
|
case "TOO_MANY_REQUESTS":
|
|
@@ -36,10 +49,10 @@ function normalizeError(err) {
|
|
|
36
49
|
case "UNPROCESSABLE_CONTENT": {
|
|
37
50
|
const issues = pickIssues(data);
|
|
38
51
|
if (issues) return { kind: "validation", issues };
|
|
39
|
-
return { kind: "server", status, message };
|
|
52
|
+
return { kind: "server", status, message, ...pickStringField(data, "code") };
|
|
40
53
|
}
|
|
41
54
|
default:
|
|
42
|
-
return { kind: "server", status, message };
|
|
55
|
+
return { kind: "server", status, message, ...pickStringField(data, "code") };
|
|
43
56
|
}
|
|
44
57
|
}
|
|
45
58
|
return { kind: "server", status: 0 };
|
|
@@ -69,6 +82,16 @@ function createEventBus() {
|
|
|
69
82
|
|
|
70
83
|
// src/auth.ts
|
|
71
84
|
var PROACTIVE_REFRESH_LEAD_MS = 6e4;
|
|
85
|
+
var REFRESH_RETRY_BASE_MS = 5e3;
|
|
86
|
+
var REFRESH_RETRY_MAX_MS = 5 * 6e4;
|
|
87
|
+
var REFRESH_MAX_RETRIES = 6;
|
|
88
|
+
function isSessionRejected(err) {
|
|
89
|
+
return err.kind === "auth_required" || err.kind === "forbidden";
|
|
90
|
+
}
|
|
91
|
+
function refreshRetryDelay(attempt, err) {
|
|
92
|
+
const backoff = Math.min(REFRESH_RETRY_MAX_MS, REFRESH_RETRY_BASE_MS * 2 ** attempt);
|
|
93
|
+
return err.kind === "rate_limited" ? Math.max(backoff, err.retryAfter) : backoff;
|
|
94
|
+
}
|
|
72
95
|
function createAuthNamespace(opts) {
|
|
73
96
|
const bus = createEventBus();
|
|
74
97
|
let cached = null;
|
|
@@ -83,18 +106,23 @@ function createAuthNamespace(opts) {
|
|
|
83
106
|
}
|
|
84
107
|
}
|
|
85
108
|
const MAX_TIMEOUT_MS = 2147483647;
|
|
86
|
-
function
|
|
109
|
+
function armRefreshTimer(delay, attempt) {
|
|
87
110
|
clearTimer();
|
|
88
|
-
const expiresMs = new Date(session.expiresAt).getTime();
|
|
89
|
-
if (Number.isNaN(expiresMs)) return;
|
|
90
|
-
const rawDelay = expiresMs - Date.now() - PROACTIVE_REFRESH_LEAD_MS;
|
|
91
|
-
const delay = Math.min(MAX_TIMEOUT_MS, Math.max(0, rawDelay));
|
|
92
111
|
timer = setTimeout(() => {
|
|
93
112
|
timer = null;
|
|
94
|
-
void refreshFn().catch(() => {
|
|
113
|
+
void refreshFn().catch((err) => {
|
|
114
|
+
if (cached === null || timer !== null || attempt >= REFRESH_MAX_RETRIES) return;
|
|
115
|
+
armRefreshTimer(refreshRetryDelay(attempt, err), attempt + 1);
|
|
95
116
|
});
|
|
96
117
|
}, delay);
|
|
97
118
|
}
|
|
119
|
+
function scheduleRefresh(session) {
|
|
120
|
+
clearTimer();
|
|
121
|
+
const expiresMs = new Date(session.expiresAt).getTime();
|
|
122
|
+
if (Number.isNaN(expiresMs)) return;
|
|
123
|
+
const rawDelay = expiresMs - Date.now() - PROACTIVE_REFRESH_LEAD_MS;
|
|
124
|
+
armRefreshTimer(Math.min(MAX_TIMEOUT_MS, Math.max(0, rawDelay)), 0);
|
|
125
|
+
}
|
|
98
126
|
function applySession(session, event) {
|
|
99
127
|
cached = session;
|
|
100
128
|
void opts.storage.set(session);
|
|
@@ -253,6 +281,15 @@ function createAuthNamespace(opts) {
|
|
|
253
281
|
const revokeOtherSessions = async () => {
|
|
254
282
|
await call(() => opts.transport.client.auth.revokeOtherSessions());
|
|
255
283
|
};
|
|
284
|
+
const closeAccount = async (input) => {
|
|
285
|
+
await call(
|
|
286
|
+
() => opts.transport.client.auth.closeAccount({
|
|
287
|
+
confirmEmail: input.confirmEmail,
|
|
288
|
+
password: input.password
|
|
289
|
+
})
|
|
290
|
+
);
|
|
291
|
+
clearSession();
|
|
292
|
+
};
|
|
256
293
|
const _refresh = async () => {
|
|
257
294
|
let current = cached;
|
|
258
295
|
if (!current) {
|
|
@@ -274,8 +311,9 @@ function createAuthNamespace(opts) {
|
|
|
274
311
|
"session_refreshed"
|
|
275
312
|
);
|
|
276
313
|
} catch (err) {
|
|
277
|
-
|
|
278
|
-
|
|
314
|
+
const normalized = normalizeError(err);
|
|
315
|
+
if (isSessionRejected(normalized)) clearSession();
|
|
316
|
+
throw normalized;
|
|
279
317
|
}
|
|
280
318
|
};
|
|
281
319
|
const _signOutLocal = () => clearSession();
|
|
@@ -303,6 +341,7 @@ function createAuthNamespace(opts) {
|
|
|
303
341
|
listSessions,
|
|
304
342
|
revokeSession,
|
|
305
343
|
revokeOtherSessions,
|
|
344
|
+
closeAccount,
|
|
306
345
|
_refresh,
|
|
307
346
|
_signOutLocal
|
|
308
347
|
};
|
|
@@ -438,15 +477,32 @@ function createTransport(options) {
|
|
|
438
477
|
};
|
|
439
478
|
}
|
|
440
479
|
var REFRESH_HEADER = "X-Fytkit-Refresh";
|
|
441
|
-
|
|
442
|
-
|
|
480
|
+
var CREDENTIAL_PROCEDURES = [
|
|
481
|
+
"auth/signIn",
|
|
482
|
+
"auth/signUp",
|
|
483
|
+
"auth/requestPasswordReset",
|
|
484
|
+
"auth/confirmPasswordReset",
|
|
485
|
+
"auth/confirmEmail",
|
|
486
|
+
"auth/verifyTotp",
|
|
487
|
+
"auth/verifyBackupCode",
|
|
488
|
+
"auth/requestEmailOtp",
|
|
489
|
+
"auth/verifyEmailOtp"
|
|
490
|
+
];
|
|
491
|
+
function requestPathname(request) {
|
|
443
492
|
try {
|
|
444
|
-
|
|
445
|
-
return path.endsWith("/auth/refresh");
|
|
493
|
+
return new URL(request.url).pathname;
|
|
446
494
|
} catch {
|
|
447
|
-
return
|
|
495
|
+
return "";
|
|
448
496
|
}
|
|
449
497
|
}
|
|
498
|
+
function isRefreshRequest(request) {
|
|
499
|
+
if (request.headers.get(REFRESH_HEADER) === "1") return true;
|
|
500
|
+
return requestPathname(request).endsWith("/auth/refresh");
|
|
501
|
+
}
|
|
502
|
+
function isCredentialRequest(request) {
|
|
503
|
+
const path = requestPathname(request);
|
|
504
|
+
return CREDENTIAL_PROCEDURES.some((procedure) => path.endsWith(`/${procedure}`));
|
|
505
|
+
}
|
|
450
506
|
function attachRefreshInterceptor(opts) {
|
|
451
507
|
let inflight = null;
|
|
452
508
|
const refreshOnce = () => {
|
|
@@ -458,7 +514,7 @@ function attachRefreshInterceptor(opts) {
|
|
|
458
514
|
return inflight;
|
|
459
515
|
};
|
|
460
516
|
opts.transport._setFetchInterceptor(async (request, next) => {
|
|
461
|
-
if (isRefreshRequest(request)) {
|
|
517
|
+
if (isRefreshRequest(request) || isCredentialRequest(request)) {
|
|
462
518
|
return next(request);
|
|
463
519
|
}
|
|
464
520
|
const buffered = await request.clone().arrayBuffer();
|
|
@@ -537,7 +593,8 @@ function createFytkitClient(options) {
|
|
|
537
593
|
changeEmail: authInstance.changeEmail,
|
|
538
594
|
listSessions: authInstance.listSessions,
|
|
539
595
|
revokeSession: authInstance.revokeSession,
|
|
540
|
-
revokeOtherSessions: authInstance.revokeOtherSessions
|
|
596
|
+
revokeOtherSessions: authInstance.revokeOtherSessions,
|
|
597
|
+
closeAccount: authInstance.closeAccount
|
|
541
598
|
},
|
|
542
599
|
...data
|
|
543
600
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/error.ts","../src/events.ts","../src/auth.ts","../src/data.ts","../src/storage.ts","../src/transport.ts","../src/index.ts"],"names":["cached"],"mappings":"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","file":"index.js","sourcesContent":["import { ORPCError } from \"@orpc/client\";\nimport type { FytkitError } from \"./types\";\n\ninterface MaybeIssuesData {\n issues?: readonly unknown[];\n}\ninterface MaybeRetryAfterData {\n retryAfterMs?: number;\n}\n\nfunction pickIssues(data: unknown): readonly unknown[] | null {\n if (!data || typeof data !== \"object\") return null;\n const issues = (data as MaybeIssuesData).issues;\n return Array.isArray(issues) ? issues : null;\n}\n\nfunction pickRetryAfter(data: unknown): number {\n if (!data || typeof data !== \"object\") return 0;\n const raw = (data as MaybeRetryAfterData).retryAfterMs;\n return typeof raw === \"number\" && raw >= 0 ? raw : 0;\n}\n\n/**\n * Map any error thrown by `@orpc/client` (or the underlying fetch) into the\n * SDK's `FytkitError` discriminated union. The SDK never re-throws raw orpc\n * or network errors at the public surface.\n */\nexport function normalizeError(err: unknown): FytkitError {\n // Network failures from fetch surface as TypeError.\n if (err instanceof TypeError) return { kind: \"network\" };\n\n if (err instanceof ORPCError) {\n const code = err.code;\n const status = err.status;\n const data: unknown = err.data;\n const message = err.message;\n\n switch (code) {\n case \"UNAUTHORIZED\":\n // Carry the server's reason (e.g. \"Invalid credentials\" on a failed\n // sign-in) so the consumer can show it instead of a generic\n // 'session expired' copy. The synthetic no-session case in auth.ts\n // is built directly and stays message-less.\n return { kind: \"auth_required\", message };\n case \"FORBIDDEN\":\n return { kind: \"forbidden\", reason: message || \"Forbidden\" };\n case \"PAYMENT_REQUIRED\":\n return { kind: \"payment_required\", message: message || \"Payment Required\" };\n case \"CONFLICT\":\n return { kind: \"conflict\", message: message || \"Conflict\" };\n case \"NOT_FOUND\":\n return { kind: \"not_found\" };\n case \"TOO_MANY_REQUESTS\":\n return { kind: \"rate_limited\", retryAfter: pickRetryAfter(data) };\n case \"BAD_REQUEST\":\n case \"UNPROCESSABLE_CONTENT\": {\n const issues = pickIssues(data);\n if (issues) return { kind: \"validation\", issues };\n // No structured issues — still carry the server's explanation so it\n // isn't dropped into a bare status code.\n return { kind: \"server\", status, message };\n }\n default:\n return { kind: \"server\", status, message };\n }\n }\n\n return { kind: \"server\", status: 0 };\n}\n","export interface EventBus<T> {\n readonly on: (listener: (value: T) => void) => () => void;\n readonly emit: (value: T) => void;\n}\n\nexport function createEventBus<T>(): EventBus<T> {\n const listeners = new Set<(value: T) => void>();\n return {\n on: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n emit: (value) => {\n for (const listener of listeners) {\n try {\n listener(value);\n } catch (err) {\n // A buggy listener must not break siblings or the calling flow.\n console.error(\"[fytkit-sdk] event listener threw\", err);\n }\n }\n },\n };\n}\n","import { normalizeError } from \"./error\";\nimport { createEventBus } from \"./events\";\nimport type { Transport } from \"./transport\";\nimport type {\n AuthState,\n FytkitError,\n SecurityStatus,\n Session,\n SessionInfo,\n SignInResult,\n TokenStorage,\n TotpEnrollment,\n TwoFactorChallenge,\n TwoFactorToken,\n} from \"./types\";\n\nexport interface AuthNamespaceOptions {\n readonly transport: Transport;\n readonly storage: TokenStorage;\n /** Called after the SDK signs out for any reason (manual, refresh failure). */\n readonly onSignedOut?: () => void;\n}\n\nexport interface AuthNamespace {\n readonly signUp: (input: { email: string; password: string; name: string }) => Promise<Session>;\n readonly signIn: (input: { email: string; password: string }) => Promise<SignInResult>;\n readonly signOut: () => Promise<void>;\n readonly getSession: () => Session | null;\n readonly onAuthStateChange: (listener: (state: AuthState) => void) => () => void;\n readonly requestPasswordReset: (input: { email: string }) => Promise<void>;\n readonly confirmPasswordReset: (input: { token: string; password: string }) => Promise<void>;\n readonly confirmEmail: (input: { token: string }) => Promise<void>;\n readonly verifyTotp: (input: {\n challenge: TwoFactorChallenge;\n code: string;\n }) => Promise<Session>;\n readonly verifyBackupCode: (input: {\n challenge: TwoFactorChallenge;\n code: string;\n }) => Promise<Session>;\n readonly requestEmailOtp: (input: { challenge: TwoFactorChallenge }) => Promise<void>;\n readonly verifyEmailOtp: (input: {\n challenge: TwoFactorChallenge;\n code: string;\n }) => Promise<Session>;\n readonly getSecurityStatus: () => Promise<SecurityStatus>;\n readonly enableTotp: (input: { password: string }) => Promise<TotpEnrollment>;\n readonly verifyTotpSetup: (input: { code: string }) => Promise<void>;\n readonly disableTwoFactor: (input: { password: string }) => Promise<void>;\n readonly regenerateBackupCodes: (input: { password: string }) => Promise<readonly string[]>;\n readonly changePassword: (input: {\n currentPassword: string;\n newPassword: string;\n revokeOtherSessions?: boolean;\n }) => Promise<void>;\n readonly changeEmail: (input: { newEmail: string }) => Promise<void>;\n readonly listSessions: () => Promise<readonly SessionInfo[]>;\n readonly revokeSession: (input: { revokeId: string }) => Promise<void>;\n readonly revokeOtherSessions: () => Promise<void>;\n /** Internal: force a refresh now. Wired by transport's 401 retry path. */\n readonly _refresh: () => Promise<Session>;\n /** Internal: clear cached session + emit signed_out. */\n readonly _signOutLocal: () => void;\n}\n\n/**\n * Schedule a proactive refresh this many ms before `expiresAt`. The\n * server-side access-token TTL is ~15 min so a 60-second lead time gives\n * us enough headroom to refresh transparently without burning sessions\n * needlessly while keeping the user signed-in across long-running tabs.\n */\nconst PROACTIVE_REFRESH_LEAD_MS = 60_000;\n\n/**\n * The auth namespace owns the cached session. It is the single writer to\n * `storage`; everything else reads via `getSession`. Calls that mutate\n * the session also emit on the event bus and (re)schedule the proactive\n * refresh timer against the new expiry.\n */\nexport function createAuthNamespace(opts: AuthNamespaceOptions): AuthNamespace {\n const bus = createEventBus<AuthState>();\n let cached: Session | null = null;\n let timer: ReturnType<typeof setTimeout> | null = null;\n // Forward declaration so scheduleRefresh can call the same _refresh body\n // the public surface exposes. Assigned at the bottom of the factory.\n let refreshFn: () => Promise<Session> = () => {\n throw new Error(\"refreshFn called before initialization\");\n };\n\n function clearTimer(): void {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n // `setTimeout` clamps any delay > 2^31-1 ms (~24.8 days) to 1 ms and\n // prints a Node deprecation warning. Sessions with an exotic far-future\n // expiry (test fixtures, mis-configured server) would otherwise fire the\n // timer instantly; cap the delay to the safe upper bound so the timer\n // simply re-arms on its next firing.\n const MAX_TIMEOUT_MS = 2_147_483_647;\n\n function scheduleRefresh(session: Session): void {\n clearTimer();\n const expiresMs = new Date(session.expiresAt).getTime();\n if (Number.isNaN(expiresMs)) return; // bad date — bail; reactive 401 path will handle it\n const rawDelay = expiresMs - Date.now() - PROACTIVE_REFRESH_LEAD_MS;\n const delay = Math.min(MAX_TIMEOUT_MS, Math.max(0, rawDelay));\n timer = setTimeout(() => {\n timer = null;\n void refreshFn().catch(() => {\n // refreshFn already handles sign-out on failure.\n });\n }, delay);\n }\n\n function applySession(session: Session, event: \"signed_in\" | \"session_refreshed\"): Session {\n cached = session;\n void opts.storage.set(session);\n bus.emit({ type: event, session });\n scheduleRefresh(session);\n return session;\n }\n\n function clearSession(emit = true): void {\n cached = null;\n clearTimer();\n void opts.storage.remove();\n if (emit) bus.emit({ type: \"signed_out\" });\n opts.onSignedOut?.();\n }\n\n async function call<T>(thunk: () => Promise<T>): Promise<T> {\n try {\n return await thunk();\n } catch (err) {\n throw normalizeError(err);\n }\n }\n\n // Best-effort hydrate at construction. If the storage already holds a\n // session (warm reload, second tab), restore it and arm the timer.\n void Promise.resolve(opts.storage.get()).then((s) => {\n if (s) {\n cached = s;\n scheduleRefresh(s);\n }\n });\n\n const signUp: AuthNamespace[\"signUp\"] = async (input) => {\n const result = await call(() => opts.transport.client.auth.signUp(input));\n const { user, session } = result.data;\n return applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"signed_in\",\n );\n };\n\n const signIn: AuthNamespace[\"signIn\"] = async (input) => {\n const result = await call(() => opts.transport.client.auth.signIn(input));\n if (result.data.kind === \"two_factor_required\") {\n // Mint the branded token exactly once at the network boundary.\n const token = result.data.twoFactorToken as TwoFactorToken;\n return {\n kind: \"two_factor_required\",\n challenge: { token, availableMethods: result.data.availableMethods },\n };\n }\n if (result.data.kind === \"signed_in\") {\n const { user, session } = result.data;\n const cached = applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"signed_in\",\n );\n return { kind: \"signed_in\", session: cached };\n }\n // Exhaustiveness guard — a future server-side variant must not silently fall through.\n const _exhaustive: never = result.data;\n throw _exhaustive;\n };\n\n const signOut: AuthNamespace[\"signOut\"] = async () => {\n const current = cached;\n if (!current) return;\n try {\n await opts.transport.client.auth.signOut({ token: current.accessToken });\n } catch (err) {\n // Even if the server rejects (e.g. token already revoked), clear\n // the local cache so the user is signed out client-side.\n void err;\n }\n clearSession();\n };\n\n const getSession: AuthNamespace[\"getSession\"] = () => cached;\n const onAuthStateChange: AuthNamespace[\"onAuthStateChange\"] = (listener) => bus.on(listener);\n\n const requestPasswordReset: AuthNamespace[\"requestPasswordReset\"] = async (input) => {\n await call(() => opts.transport.client.auth.requestPasswordReset(input));\n };\n const confirmPasswordReset: AuthNamespace[\"confirmPasswordReset\"] = async (input) => {\n await call(() => opts.transport.client.auth.confirmPasswordReset(input));\n };\n const confirmEmail: AuthNamespace[\"confirmEmail\"] = async (input) => {\n await call(() => opts.transport.client.auth.confirmEmail(input));\n };\n\n // --- Two-factor sign-in completion -----------------------------------------\n //\n // Each consumes the challenge minted by `signIn` and, on success, caches the\n // issued session exactly like the signed_in branch of `signIn`.\n\n const verifyTotp: AuthNamespace[\"verifyTotp\"] = async (input) => {\n const result = await call(() =>\n opts.transport.client.auth.verifyTotp({ token: input.challenge.token, code: input.code }),\n );\n const { user, session } = result.data;\n return applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"signed_in\",\n );\n };\n\n const verifyBackupCode: AuthNamespace[\"verifyBackupCode\"] = async (input) => {\n const result = await call(() =>\n opts.transport.client.auth.verifyBackupCode({\n token: input.challenge.token,\n code: input.code,\n }),\n );\n const { user, session } = result.data;\n return applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"signed_in\",\n );\n };\n\n const requestEmailOtp: AuthNamespace[\"requestEmailOtp\"] = async (input) => {\n await call(() => opts.transport.client.auth.requestEmailOtp({ token: input.challenge.token }));\n };\n\n const verifyEmailOtp: AuthNamespace[\"verifyEmailOtp\"] = async (input) => {\n const result = await call(() =>\n opts.transport.client.auth.verifyEmailOtp({ token: input.challenge.token, code: input.code }),\n );\n const { user, session } = result.data;\n return applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"signed_in\",\n );\n };\n\n /**\n * The server rotated the calling session and returned the replacement token\n * (TOTP-setup verification and password change with revokeOtherSessions do\n * this). Swap the cached token immediately — the old bearer is already dead,\n * so any request between now and the swap would 401. `expiresAt` is carried\n * over from the old session; it is at worst *earlier* than the new session's\n * real expiry, so the proactive refresh timer fires early and corrects it.\n */\n function adoptRotatedToken(accessToken: string): void {\n const current = cached;\n if (!current || !accessToken || accessToken === current.accessToken) return;\n applySession({ ...current, accessToken }, \"session_refreshed\");\n }\n\n const getSecurityStatus: AuthNamespace[\"getSecurityStatus\"] = async () => {\n const result = await call(() => opts.transport.client.auth.getSecurityStatus());\n return result.data;\n };\n\n const enableTotp: AuthNamespace[\"enableTotp\"] = async (input) => {\n const result = await call(() => opts.transport.client.auth.enableTotp(input));\n return result.data;\n };\n\n const verifyTotpSetup: AuthNamespace[\"verifyTotpSetup\"] = async (input) => {\n const result = await call(() => opts.transport.client.auth.verifyTotpSetup(input));\n adoptRotatedToken(result.data.accessToken);\n };\n\n const disableTwoFactor: AuthNamespace[\"disableTwoFactor\"] = async (input) => {\n await call(() => opts.transport.client.auth.disableTwoFactor(input));\n };\n\n const regenerateBackupCodes: AuthNamespace[\"regenerateBackupCodes\"] = async (input) => {\n const result = await call(() => opts.transport.client.auth.regenerateBackupCodes(input));\n return result.data.backupCodes;\n };\n\n // --- Account security -------------------------------------------------------\n\n const changePassword: AuthNamespace[\"changePassword\"] = async (input) => {\n const result = await call(() =>\n opts.transport.client.auth.changePassword({\n currentPassword: input.currentPassword,\n newPassword: input.newPassword,\n revokeOtherSessions: input.revokeOtherSessions,\n }),\n );\n adoptRotatedToken(result.data.accessToken);\n };\n\n const changeEmail: AuthNamespace[\"changeEmail\"] = async (input) => {\n await call(() => opts.transport.client.auth.changeEmail(input));\n };\n\n const listSessions: AuthNamespace[\"listSessions\"] = async () => {\n const result = await call(() => opts.transport.client.auth.listSessions());\n return result.data;\n };\n\n const revokeSession: AuthNamespace[\"revokeSession\"] = async (input) => {\n await call(() => opts.transport.client.auth.revokeSession(input));\n };\n\n const revokeOtherSessions: AuthNamespace[\"revokeOtherSessions\"] = async () => {\n await call(() => opts.transport.client.auth.revokeOtherSessions());\n };\n\n const _refresh: AuthNamespace[\"_refresh\"] = async () => {\n // Re-read storage as a fallback so a session written after\n // construction (e.g. on tab restore or in tests) is honoured.\n let current = cached;\n if (!current) {\n const fromStorage = await Promise.resolve(opts.storage.get());\n if (fromStorage) {\n cached = fromStorage;\n current = fromStorage;\n }\n }\n if (!current) {\n const err: FytkitError = { kind: \"auth_required\" };\n throw err;\n }\n try {\n const result = await opts.transport.client.auth.refresh({ token: current.accessToken });\n const { user, session } = result.data;\n return applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"session_refreshed\",\n );\n } catch (err) {\n clearSession();\n throw normalizeError(err);\n }\n };\n\n const _signOutLocal: AuthNamespace[\"_signOutLocal\"] = () => clearSession();\n\n refreshFn = _refresh;\n\n return {\n signUp,\n signIn,\n signOut,\n getSession,\n onAuthStateChange,\n requestPasswordReset,\n confirmPasswordReset,\n confirmEmail,\n verifyTotp,\n verifyBackupCode,\n requestEmailOtp,\n verifyEmailOtp,\n getSecurityStatus,\n enableTotp,\n verifyTotpSetup,\n disableTwoFactor,\n regenerateBackupCodes,\n changePassword,\n changeEmail,\n listSessions,\n revokeSession,\n revokeOtherSessions,\n _refresh,\n _signOutLocal,\n };\n}\n","import { normalizeError } from \"./error\";\nimport type { Transport } from \"./transport\";\n\n/**\n * Wrap every method on a public-router slice so thrown ORPCErrors become\n * normalised FytkitErrors at the SDK boundary.\n *\n * The slice we receive is an `@orpc/client` Proxy whose own keys are not\n * enumerable — every property lookup is intercepted lazily. We mirror that\n * shape by returning a Proxy of our own that wraps each function on first\n * access and caches the result. This way the SDK never needs to know the\n * method names ahead of time, and the typed shape from the server router\n * flows through unchanged.\n */\nfunction wrapSlice<T extends object>(slice: T): T {\n const cache = new Map<string | symbol, unknown>();\n return new Proxy(slice, {\n get(target, prop, receiver) {\n if (cache.has(prop)) return cache.get(prop);\n const value = Reflect.get(target, prop, receiver);\n if (typeof value !== \"function\") {\n // Allow nested namespaces to flow through unchanged (the orpc\n // proxy returns another proxy here, not a plain object).\n return value;\n }\n const wrapped = async (...args: unknown[]) => {\n try {\n return await (value as (...a: unknown[]) => Promise<unknown>)(...args);\n } catch (err) {\n throw normalizeError(err);\n }\n };\n cache.set(prop, wrapped);\n return wrapped;\n },\n }) as T;\n}\n\nexport interface DataNamespaces {\n readonly workouts: Transport[\"client\"][\"workout\"];\n readonly exercises: Transport[\"client\"][\"exercise\"];\n readonly programs: Transport[\"client\"][\"program\"];\n readonly goals: Transport[\"client\"][\"goal\"];\n readonly equipment: Transport[\"client\"][\"equipment\"];\n readonly recipes: Transport[\"client\"][\"recipe\"];\n readonly nutrition: Transport[\"client\"][\"nutrition\"];\n readonly articles: Transport[\"client\"][\"article\"];\n readonly enrollments: Transport[\"client\"][\"enrollment\"];\n readonly favourites: Transport[\"client\"][\"favourite\"];\n readonly sessionLogs: Transport[\"client\"][\"sessionLog\"];\n readonly progression: Transport[\"client\"][\"progression\"];\n readonly bodyMetrics: Transport[\"client\"][\"bodyMetric\"];\n readonly progress: Transport[\"client\"][\"progress\"];\n readonly media: Transport[\"client\"][\"media\"];\n readonly appConfig: Transport[\"client\"][\"appConfig\"];\n readonly subscription: Transport[\"client\"][\"subscription\"];\n readonly profile: Transport[\"client\"][\"profile\"];\n readonly nutritionLog: Transport[\"client\"][\"nutritionLog\"];\n readonly preferences: Transport[\"client\"][\"preferences\"];\n}\n\nexport function createDataNamespaces(transport: Transport): DataNamespaces {\n const c = transport.client;\n return {\n workouts: wrapSlice(c.workout),\n exercises: wrapSlice(c.exercise),\n programs: wrapSlice(c.program),\n goals: wrapSlice(c.goal),\n equipment: wrapSlice(c.equipment),\n recipes: wrapSlice(c.recipe),\n nutrition: wrapSlice(c.nutrition),\n articles: wrapSlice(c.article),\n enrollments: wrapSlice(c.enrollment),\n favourites: wrapSlice(c.favourite),\n sessionLogs: wrapSlice(c.sessionLog),\n progression: wrapSlice(c.progression),\n bodyMetrics: wrapSlice(c.bodyMetric),\n progress: wrapSlice(c.progress),\n media: wrapSlice(c.media),\n appConfig: wrapSlice(c.appConfig),\n subscription: wrapSlice(c.subscription),\n profile: wrapSlice(c.profile),\n nutritionLog: wrapSlice(c.nutritionLog),\n preferences: wrapSlice(c.preferences),\n };\n}\n","import type { Session, TokenStorage } from \"./types\";\n\nconst STORAGE_KEY_PREFIX = \"fytkit:\";\nconst STORAGE_KEY_SUFFIX = \":tokens\";\n\nfunction keyFor(clientId: string): string {\n return `${STORAGE_KEY_PREFIX}${clientId}${STORAGE_KEY_SUFFIX}`;\n}\n\nfunction isValidSession(value: unknown): value is Session {\n if (!value || typeof value !== \"object\") return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.accessToken === \"string\" &&\n typeof v.expiresAt === \"string\" &&\n typeof v.user === \"object\" &&\n v.user !== null &&\n typeof (v.user as { id?: unknown }).id === \"string\" &&\n typeof (v.user as { email?: unknown }).email === \"string\"\n );\n}\n\n/**\n * Default web storage. Reads/writes JSON under `fytkit:{clientId}:tokens`.\n * Silently no-ops when localStorage throws (Safari private mode, SSR).\n */\nexport function createLocalStorageAdapter(clientId: string): TokenStorage {\n const key = keyFor(clientId);\n return {\n get: () => {\n try {\n const raw = globalThis.localStorage?.getItem(key);\n if (!raw) return null;\n const parsed: unknown = JSON.parse(raw);\n return isValidSession(parsed) ? parsed : null;\n } catch {\n return null;\n }\n },\n set: (session: Session) => {\n try {\n globalThis.localStorage?.setItem(key, JSON.stringify(session));\n } catch {\n // Quota / private-mode / SSR — swallow and let the caller treat the\n // session as in-memory-only for this tab.\n }\n },\n remove: () => {\n try {\n globalThis.localStorage?.removeItem(key);\n } catch {\n // see set() rationale\n }\n },\n };\n}\n\n/** In-memory storage. Useful for tests and SSR. */\nexport function createMemoryStorageAdapter(): TokenStorage {\n let current: Session | null = null;\n return {\n get: () => current,\n set: (session: Session) => {\n current = session;\n },\n remove: () => {\n current = null;\n },\n };\n}\n","import type { PublicApi } from \"@fytkit/types\";\nimport { createORPCClient } from \"@orpc/client\";\nimport { RPCLink } from \"@orpc/client/fetch\";\nimport type { Session } from \"./types\";\n\nexport interface CreateTransportOptions {\n readonly baseUrl: string;\n readonly clientId: string;\n readonly getSession: () => Session | null;\n readonly fetch?: typeof fetch;\n}\n\nexport interface Transport {\n readonly client: PublicApi;\n /** Internal: hook for attachRefreshInterceptor to install a fetch wrapper. */\n readonly _setFetchInterceptor: (interceptor: FetchInterceptor) => void;\n}\n\ntype FetchInterceptor = (\n request: Request,\n next: (req: Request) => Promise<Response>,\n) => Promise<Response>;\n\nexport function createTransport(options: CreateTransportOptions): Transport {\n let interceptor: FetchInterceptor | null = null;\n const baseFetch: typeof fetch = options.fetch ?? fetch.bind(globalThis);\n\n // The fetch handed to RPCLink resolves the interceptor lazily so callers\n // can install one after construction (see attachRefreshInterceptor).\n const wrappedFetch: typeof fetch = (input, init) => {\n const request = input instanceof Request ? input : new Request(input, init);\n const next = (req: Request) => baseFetch(req);\n if (!interceptor) return next(request);\n return interceptor(request, next);\n };\n\n const link = new RPCLink({\n url: () => `${options.baseUrl}/rpc`,\n headers: () => {\n const session = options.getSession();\n const headers: Record<string, string> = {\n \"X-Fytkit-Client-Id\": options.clientId,\n };\n if (session) {\n headers.Authorization = `Bearer ${session.accessToken}`;\n }\n return headers;\n },\n fetch: wrappedFetch,\n });\n\n const client: PublicApi = createORPCClient(link);\n return {\n client,\n _setFetchInterceptor: (i) => {\n interceptor = i;\n },\n };\n}\n\n/** Internal marker header — set on the refresh call so we never recurse. */\nconst REFRESH_HEADER = \"X-Fytkit-Refresh\";\n\n/**\n * Detect whether a request targets the refresh procedure. The RPCLink\n * routes per-procedure calls under a URL whose pathname ends with the\n * procedure path (joined as `/segment/segment`), so we look for the\n * `auth/refresh` suffix. We use endsWith so trailing query strings or\n * link-level prefixes do not break detection.\n */\nfunction isRefreshRequest(request: Request): boolean {\n if (request.headers.get(REFRESH_HEADER) === \"1\") return true;\n try {\n const path = new URL(request.url).pathname;\n return path.endsWith(\"/auth/refresh\");\n } catch {\n return false;\n }\n}\n\nexport interface AttachRefreshInterceptorOptions {\n readonly transport: Transport;\n readonly auth: {\n readonly _refresh: () => Promise<Session>;\n readonly _signOutLocal: () => void;\n readonly getSession: () => Session | null;\n };\n}\n\n/**\n * Install a fetch interceptor that, on a 401 response, calls auth._refresh\n * once and retries the original request. Concurrent 401s de-duplicate\n * onto a single in-flight refresh promise. A 401 from refresh itself or\n * from the retry triggers a local sign-out and propagates the original\n * 401 to the caller (after which orpc throws ORPCError(\"UNAUTHORIZED\")).\n */\nexport function attachRefreshInterceptor(opts: AttachRefreshInterceptorOptions): void {\n let inflight: Promise<Session> | null = null;\n\n const refreshOnce = (): Promise<Session> => {\n if (!inflight) {\n inflight = opts.auth._refresh().finally(() => {\n inflight = null;\n });\n }\n return inflight;\n };\n\n opts.transport._setFetchInterceptor(async (request, next) => {\n // Refresh calls must never trigger a recursive refresh.\n if (isRefreshRequest(request)) {\n return next(request);\n }\n\n // Capture the request body now, before we pass `request` to `next` —\n // once fetch consumes a Request body it can no longer be cloned.\n const buffered = await request.clone().arrayBuffer();\n\n const firstResponse = await next(request);\n if (firstResponse.status !== 401) return firstResponse;\n\n try {\n await refreshOnce();\n } catch {\n // _refresh already called clearSession + emitted signed_out.\n return firstResponse;\n }\n\n // Rebuild headers so the new Authorization header replaces the stale\n // one — the RPCLink only re-evaluates its own `headers()` once per call,\n // so we patch the bearer ourselves here.\n const replayedHeaders = new Headers(request.headers);\n const session = opts.auth.getSession();\n if (session) {\n replayedHeaders.set(\"Authorization\", `Bearer ${session.accessToken}`);\n } else {\n replayedHeaders.delete(\"Authorization\");\n }\n const replayed = new Request(request.url, {\n method: request.method,\n headers: replayedHeaders,\n body: buffered.byteLength > 0 ? buffered : undefined,\n credentials: request.credentials,\n mode: request.mode,\n cache: request.cache,\n redirect: request.redirect,\n referrer: request.referrer,\n integrity: request.integrity,\n });\n\n const retried = await next(replayed);\n if (retried.status === 401) {\n // Refresh succeeded server-side but the server still rejects — the\n // session is dead. Sign out locally and let orpc surface the 401.\n opts.auth._signOutLocal();\n }\n return retried;\n });\n}\n","import { createAuthNamespace } from \"./auth\";\nimport { createDataNamespaces } from \"./data\";\nimport { createLocalStorageAdapter, createMemoryStorageAdapter } from \"./storage\";\nimport { attachRefreshInterceptor, createTransport } from \"./transport\";\nimport type { FytkitClient, FytkitClientOptions } from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://api.fytkit.com\";\n\nexport function createFytkitClient(options: FytkitClientOptions): FytkitClient {\n if (!options || typeof options.clientId !== \"string\" || options.clientId.length === 0) {\n throw new Error(\"createFytkitClient: `clientId` is required\");\n }\n\n const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n const storage = options.storage ?? createLocalStorageAdapter(options.clientId);\n\n // `auth` is declared after `transport` because `createAuthNamespace`\n // needs the transport to make its RPC calls. The transport in turn needs\n // a `getSession` that reads the auth cache — we resolve the cycle with a\n // late-bound reference: the closure below reads `auth` lazily, which is\n // safe because no request can fire before construction finishes.\n let auth: ReturnType<typeof createAuthNamespace> | null = null;\n\n const transport = createTransport({\n baseUrl,\n clientId: options.clientId,\n getSession: () => auth?.getSession() ?? null,\n fetch: options.fetch,\n });\n\n const authInstance = createAuthNamespace({ transport, storage });\n auth = authInstance;\n attachRefreshInterceptor({ transport, auth: authInstance });\n\n const data = createDataNamespaces(transport);\n\n return {\n auth: {\n signUp: authInstance.signUp,\n signIn: authInstance.signIn,\n signOut: authInstance.signOut,\n getSession: authInstance.getSession,\n onAuthStateChange: authInstance.onAuthStateChange,\n requestPasswordReset: authInstance.requestPasswordReset,\n confirmPasswordReset: authInstance.confirmPasswordReset,\n confirmEmail: authInstance.confirmEmail,\n verifyTotp: authInstance.verifyTotp,\n verifyBackupCode: authInstance.verifyBackupCode,\n requestEmailOtp: authInstance.requestEmailOtp,\n verifyEmailOtp: authInstance.verifyEmailOtp,\n getSecurityStatus: authInstance.getSecurityStatus,\n enableTotp: authInstance.enableTotp,\n verifyTotpSetup: authInstance.verifyTotpSetup,\n disableTwoFactor: authInstance.disableTwoFactor,\n regenerateBackupCodes: authInstance.regenerateBackupCodes,\n changePassword: authInstance.changePassword,\n changeEmail: authInstance.changeEmail,\n listSessions: authInstance.listSessions,\n revokeSession: authInstance.revokeSession,\n revokeOtherSessions: authInstance.revokeOtherSessions,\n },\n ...data,\n };\n}\n\n// Public re-exports.\nexport { createLocalStorageAdapter, createMemoryStorageAdapter };\nexport type {\n AuthState,\n FytkitClient,\n FytkitClientOptions,\n FytkitError,\n SecurityStatus,\n Session,\n SessionInfo,\n SignInResult,\n TokenStorage,\n TotpEnrollment,\n TwoFactorChallenge,\n TwoFactorMethod,\n TwoFactorToken,\n} from \"./types\";\n// `PublicApi` is the inferred router type that backs every namespace\n// on `FytkitClient` (e.g. `client.workouts` is `PublicApi[\"workout\"]`).\n// Re-exported so consumers can write generics over the router shape —\n// previously this surfaced as an `ae-forgotten-export` warning on\n// `FytkitClient` in the api-extractor report. The api-extractor will\n// still warn about `PublicRouter`/`publicRouter` being unexported —\n// that is intentional, those are implementation-detail names from the\n// generated snapshot that should not be part of the consumer surface.\nexport type { PublicApi } from \"@fytkit/types\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/error.ts","../src/events.ts","../src/auth.ts","../src/data.ts","../src/storage.ts","../src/transport.ts","../src/index.ts"],"names":["cached"],"mappings":"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","file":"index.js","sourcesContent":["import { ORPCError } from \"@orpc/client\";\nimport type { FytkitError } from \"./types\";\n\ninterface MaybeIssuesData {\n issues?: readonly unknown[];\n}\ninterface MaybeRetryAfterData {\n retryAfterMs?: number;\n}\n\nfunction pickIssues(data: unknown): readonly unknown[] | null {\n if (!data || typeof data !== \"object\") return null;\n const issues = (data as MaybeIssuesData).issues;\n return Array.isArray(issues) ? issues : null;\n}\n\nfunction pickRetryAfter(data: unknown): number {\n if (!data || typeof data !== \"object\") return 0;\n const raw = (data as MaybeRetryAfterData).retryAfterMs;\n return typeof raw === \"number\" && raw >= 0 ? raw : 0;\n}\n\n/**\n * A non-empty string field of the error's `data`, when the server sent one:\n * `code` (the machine-readable reason, e.g. \"invalid_password\") or\n * `enrollmentId` (the existing enrolment on \"Already enrolled\"). Returned as\n * a spread-able object so kinds only gain the key when there is a value —\n * existing `toEqual` shapes stay intact.\n */\nfunction pickStringField<K extends \"code\" | \"enrollmentId\">(\n data: unknown,\n key: K,\n): { [P in K]?: string } {\n if (!data || typeof data !== \"object\") return {};\n const raw = (data as Partial<Record<K, unknown>>)[key];\n return typeof raw === \"string\" && raw.length > 0 ? ({ [key]: raw } as { [P in K]?: string }) : {};\n}\n\n/**\n * Map any error thrown by `@orpc/client` (or the underlying fetch) into the\n * SDK's `FytkitError` discriminated union. The SDK never re-throws raw orpc\n * or network errors at the public surface.\n */\nexport function normalizeError(err: unknown): FytkitError {\n // Network failures from fetch surface as TypeError.\n if (err instanceof TypeError) return { kind: \"network\" };\n\n if (err instanceof ORPCError) {\n const code = err.code;\n const status = err.status;\n const data: unknown = err.data;\n const message = err.message;\n\n switch (code) {\n case \"UNAUTHORIZED\":\n // Carry the server's reason (e.g. \"Invalid credentials\" on a failed\n // sign-in) so the consumer can show it instead of a generic\n // 'session expired' copy. The synthetic no-session case in auth.ts\n // is built directly and stays message-less.\n return { kind: \"auth_required\", message };\n case \"FORBIDDEN\":\n return {\n kind: \"forbidden\",\n reason: message || \"Forbidden\",\n ...pickStringField(data, \"code\"),\n };\n case \"PAYMENT_REQUIRED\":\n return { kind: \"payment_required\", message: message || \"Payment Required\" };\n case \"CONFLICT\":\n return {\n kind: \"conflict\",\n message: message || \"Conflict\",\n ...pickStringField(data, \"enrollmentId\"),\n };\n case \"NOT_FOUND\":\n return { kind: \"not_found\" };\n case \"TOO_MANY_REQUESTS\":\n return { kind: \"rate_limited\", retryAfter: pickRetryAfter(data) };\n case \"BAD_REQUEST\":\n case \"UNPROCESSABLE_CONTENT\": {\n const issues = pickIssues(data);\n if (issues) return { kind: \"validation\", issues };\n // No structured issues — still carry the server's explanation (and\n // its code, e.g. \"invalid_password\") so it isn't dropped into a bare\n // status code.\n return { kind: \"server\", status, message, ...pickStringField(data, \"code\") };\n }\n default:\n return { kind: \"server\", status, message, ...pickStringField(data, \"code\") };\n }\n }\n\n return { kind: \"server\", status: 0 };\n}\n","export interface EventBus<T> {\n readonly on: (listener: (value: T) => void) => () => void;\n readonly emit: (value: T) => void;\n}\n\nexport function createEventBus<T>(): EventBus<T> {\n const listeners = new Set<(value: T) => void>();\n return {\n on: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n emit: (value) => {\n for (const listener of listeners) {\n try {\n listener(value);\n } catch (err) {\n // A buggy listener must not break siblings or the calling flow.\n console.error(\"[fytkit-sdk] event listener threw\", err);\n }\n }\n },\n };\n}\n","import { normalizeError } from \"./error\";\nimport { createEventBus } from \"./events\";\nimport type { Transport } from \"./transport\";\nimport type {\n AuthState,\n FytkitError,\n SecurityStatus,\n Session,\n SessionInfo,\n SignInResult,\n TokenStorage,\n TotpEnrollment,\n TwoFactorChallenge,\n TwoFactorToken,\n} from \"./types\";\n\nexport interface AuthNamespaceOptions {\n readonly transport: Transport;\n readonly storage: TokenStorage;\n /** Called after the SDK signs out for any reason (manual, refresh failure). */\n readonly onSignedOut?: () => void;\n}\n\nexport interface AuthNamespace {\n readonly signUp: (input: { email: string; password: string; name: string }) => Promise<Session>;\n readonly signIn: (input: { email: string; password: string }) => Promise<SignInResult>;\n readonly signOut: () => Promise<void>;\n readonly getSession: () => Session | null;\n readonly onAuthStateChange: (listener: (state: AuthState) => void) => () => void;\n readonly requestPasswordReset: (input: { email: string }) => Promise<void>;\n readonly confirmPasswordReset: (input: { token: string; password: string }) => Promise<void>;\n readonly confirmEmail: (input: { token: string }) => Promise<void>;\n readonly verifyTotp: (input: { challenge: TwoFactorChallenge; code: string }) => Promise<Session>;\n readonly verifyBackupCode: (input: {\n challenge: TwoFactorChallenge;\n code: string;\n }) => Promise<Session>;\n readonly requestEmailOtp: (input: { challenge: TwoFactorChallenge }) => Promise<void>;\n readonly verifyEmailOtp: (input: {\n challenge: TwoFactorChallenge;\n code: string;\n }) => Promise<Session>;\n readonly getSecurityStatus: () => Promise<SecurityStatus>;\n readonly enableTotp: (input: { password: string }) => Promise<TotpEnrollment>;\n readonly verifyTotpSetup: (input: { code: string }) => Promise<void>;\n readonly disableTwoFactor: (input: { password: string }) => Promise<void>;\n readonly regenerateBackupCodes: (input: { password: string }) => Promise<readonly string[]>;\n readonly changePassword: (input: {\n currentPassword: string;\n newPassword: string;\n revokeOtherSessions?: boolean;\n }) => Promise<void>;\n readonly changeEmail: (input: { newEmail: string }) => Promise<void>;\n readonly listSessions: () => Promise<readonly SessionInfo[]>;\n readonly revokeSession: (input: { revokeId: string }) => Promise<void>;\n readonly revokeOtherSessions: () => Promise<void>;\n readonly closeAccount: (input: { confirmEmail: string; password: string }) => Promise<void>;\n /** Internal: force a refresh now. Wired by transport's 401 retry path. */\n readonly _refresh: () => Promise<Session>;\n /** Internal: clear cached session + emit signed_out. */\n readonly _signOutLocal: () => void;\n}\n\n/**\n * Schedule a proactive refresh this many ms before `expiresAt`. The token\n * is a server session (7 days, extended on use), and a 60-second lead time\n * refreshes it transparently in long-running tabs without refreshing\n * needlessly.\n */\nconst PROACTIVE_REFRESH_LEAD_MS = 60_000;\n\n/**\n * When a proactive refresh fails for a reason other than the server rejecting\n * the token (offline, 5xx, rate limit), the session is kept and the timer\n * retries with exponential backoff: 5 s, 10 s, 20 s … capped at 5 min, for\n * at most this many attempts. After that the SDK stops polling; the next\n * request's reactive 401 path takes over if the token has actually lapsed.\n */\nconst REFRESH_RETRY_BASE_MS = 5_000;\nconst REFRESH_RETRY_MAX_MS = 5 * 60_000;\nconst REFRESH_MAX_RETRIES = 6;\n\n/**\n * Only an explicit server rejection of the token (401/403) ends the session.\n * Network failures, 5xx and rate limits say nothing about the token, so\n * signing the member out on them would be wrong — and on a phone that drops\n * signal it signs members out constantly.\n */\nfunction isSessionRejected(err: FytkitError): boolean {\n return err.kind === \"auth_required\" || err.kind === \"forbidden\";\n}\n\nfunction refreshRetryDelay(attempt: number, err: FytkitError): number {\n const backoff = Math.min(REFRESH_RETRY_MAX_MS, REFRESH_RETRY_BASE_MS * 2 ** attempt);\n // Honour the server's rate-limit window when it is longer than our backoff.\n return err.kind === \"rate_limited\" ? Math.max(backoff, err.retryAfter) : backoff;\n}\n\n/**\n * The auth namespace owns the cached session. It is the single writer to\n * `storage`; everything else reads via `getSession`. Calls that mutate\n * the session also emit on the event bus and (re)schedule the proactive\n * refresh timer against the new expiry.\n */\nexport function createAuthNamespace(opts: AuthNamespaceOptions): AuthNamespace {\n const bus = createEventBus<AuthState>();\n let cached: Session | null = null;\n let timer: ReturnType<typeof setTimeout> | null = null;\n // Forward declaration so scheduleRefresh can call the same _refresh body\n // the public surface exposes. Assigned at the bottom of the factory.\n let refreshFn: () => Promise<Session> = () => {\n throw new Error(\"refreshFn called before initialization\");\n };\n\n function clearTimer(): void {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n // `setTimeout` clamps any delay > 2^31-1 ms (~24.8 days) to 1 ms and\n // prints a Node deprecation warning. Sessions with an exotic far-future\n // expiry (test fixtures, mis-configured server) would otherwise fire the\n // timer instantly; cap the delay to the safe upper bound so the timer\n // simply re-arms on its next firing.\n const MAX_TIMEOUT_MS = 2_147_483_647;\n\n // `attempt` counts consecutive failed proactive refreshes; 0 for the\n // expiry-driven firing, 1+ for backoff retries.\n function armRefreshTimer(delay: number, attempt: number): void {\n clearTimer();\n timer = setTimeout(() => {\n timer = null;\n void refreshFn().catch((err: FytkitError) => {\n // A rejected token already cleared the session inside _refresh.\n // Otherwise keep it and retry — unless something re-armed the timer\n // meanwhile (a sign-in, a rotated token) or we are out of attempts.\n if (cached === null || timer !== null || attempt >= REFRESH_MAX_RETRIES) return;\n armRefreshTimer(refreshRetryDelay(attempt, err), attempt + 1);\n });\n }, delay);\n }\n\n function scheduleRefresh(session: Session): void {\n clearTimer();\n const expiresMs = new Date(session.expiresAt).getTime();\n if (Number.isNaN(expiresMs)) return; // bad date — bail; reactive 401 path will handle it\n const rawDelay = expiresMs - Date.now() - PROACTIVE_REFRESH_LEAD_MS;\n armRefreshTimer(Math.min(MAX_TIMEOUT_MS, Math.max(0, rawDelay)), 0);\n }\n\n function applySession(session: Session, event: \"signed_in\" | \"session_refreshed\"): Session {\n cached = session;\n void opts.storage.set(session);\n bus.emit({ type: event, session });\n scheduleRefresh(session);\n return session;\n }\n\n function clearSession(emit = true): void {\n cached = null;\n clearTimer();\n void opts.storage.remove();\n if (emit) bus.emit({ type: \"signed_out\" });\n opts.onSignedOut?.();\n }\n\n async function call<T>(thunk: () => Promise<T>): Promise<T> {\n try {\n return await thunk();\n } catch (err) {\n throw normalizeError(err);\n }\n }\n\n // Best-effort hydrate at construction. If the storage already holds a\n // session (warm reload, second tab), restore it and arm the timer.\n void Promise.resolve(opts.storage.get()).then((s) => {\n if (s) {\n cached = s;\n scheduleRefresh(s);\n }\n });\n\n const signUp: AuthNamespace[\"signUp\"] = async (input) => {\n const result = await call(() => opts.transport.client.auth.signUp(input));\n const { user, session } = result.data;\n return applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"signed_in\",\n );\n };\n\n const signIn: AuthNamespace[\"signIn\"] = async (input) => {\n const result = await call(() => opts.transport.client.auth.signIn(input));\n if (result.data.kind === \"two_factor_required\") {\n // Mint the branded token exactly once at the network boundary.\n const token = result.data.twoFactorToken as TwoFactorToken;\n return {\n kind: \"two_factor_required\",\n challenge: { token, availableMethods: result.data.availableMethods },\n };\n }\n if (result.data.kind === \"signed_in\") {\n const { user, session } = result.data;\n const cached = applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"signed_in\",\n );\n return { kind: \"signed_in\", session: cached };\n }\n // Exhaustiveness guard — a future server-side variant must not silently fall through.\n const _exhaustive: never = result.data;\n throw _exhaustive;\n };\n\n const signOut: AuthNamespace[\"signOut\"] = async () => {\n const current = cached;\n if (!current) return;\n try {\n await opts.transport.client.auth.signOut({ token: current.accessToken });\n } catch (err) {\n // Even if the server rejects (e.g. token already revoked), clear\n // the local cache so the user is signed out client-side.\n void err;\n }\n clearSession();\n };\n\n const getSession: AuthNamespace[\"getSession\"] = () => cached;\n const onAuthStateChange: AuthNamespace[\"onAuthStateChange\"] = (listener) => bus.on(listener);\n\n const requestPasswordReset: AuthNamespace[\"requestPasswordReset\"] = async (input) => {\n await call(() => opts.transport.client.auth.requestPasswordReset(input));\n };\n const confirmPasswordReset: AuthNamespace[\"confirmPasswordReset\"] = async (input) => {\n await call(() => opts.transport.client.auth.confirmPasswordReset(input));\n };\n const confirmEmail: AuthNamespace[\"confirmEmail\"] = async (input) => {\n await call(() => opts.transport.client.auth.confirmEmail(input));\n };\n\n // --- Two-factor sign-in completion -----------------------------------------\n //\n // Each consumes the challenge minted by `signIn` and, on success, caches the\n // issued session exactly like the signed_in branch of `signIn`.\n\n const verifyTotp: AuthNamespace[\"verifyTotp\"] = async (input) => {\n const result = await call(() =>\n opts.transport.client.auth.verifyTotp({ token: input.challenge.token, code: input.code }),\n );\n const { user, session } = result.data;\n return applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"signed_in\",\n );\n };\n\n const verifyBackupCode: AuthNamespace[\"verifyBackupCode\"] = async (input) => {\n const result = await call(() =>\n opts.transport.client.auth.verifyBackupCode({\n token: input.challenge.token,\n code: input.code,\n }),\n );\n const { user, session } = result.data;\n return applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"signed_in\",\n );\n };\n\n const requestEmailOtp: AuthNamespace[\"requestEmailOtp\"] = async (input) => {\n await call(() => opts.transport.client.auth.requestEmailOtp({ token: input.challenge.token }));\n };\n\n const verifyEmailOtp: AuthNamespace[\"verifyEmailOtp\"] = async (input) => {\n const result = await call(() =>\n opts.transport.client.auth.verifyEmailOtp({ token: input.challenge.token, code: input.code }),\n );\n const { user, session } = result.data;\n return applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"signed_in\",\n );\n };\n\n /**\n * The server rotated the calling session and returned the replacement token\n * (TOTP-setup verification and password change with revokeOtherSessions do\n * this). Swap the cached token immediately — the old bearer is already dead,\n * so any request between now and the swap would 401. `expiresAt` is carried\n * over from the old session; it is at worst *earlier* than the new session's\n * real expiry, so the proactive refresh timer fires early and corrects it.\n */\n function adoptRotatedToken(accessToken: string): void {\n const current = cached;\n if (!current || !accessToken || accessToken === current.accessToken) return;\n applySession({ ...current, accessToken }, \"session_refreshed\");\n }\n\n const getSecurityStatus: AuthNamespace[\"getSecurityStatus\"] = async () => {\n const result = await call(() => opts.transport.client.auth.getSecurityStatus());\n return result.data;\n };\n\n const enableTotp: AuthNamespace[\"enableTotp\"] = async (input) => {\n const result = await call(() => opts.transport.client.auth.enableTotp(input));\n return result.data;\n };\n\n const verifyTotpSetup: AuthNamespace[\"verifyTotpSetup\"] = async (input) => {\n const result = await call(() => opts.transport.client.auth.verifyTotpSetup(input));\n adoptRotatedToken(result.data.accessToken);\n };\n\n const disableTwoFactor: AuthNamespace[\"disableTwoFactor\"] = async (input) => {\n await call(() => opts.transport.client.auth.disableTwoFactor(input));\n };\n\n const regenerateBackupCodes: AuthNamespace[\"regenerateBackupCodes\"] = async (input) => {\n const result = await call(() => opts.transport.client.auth.regenerateBackupCodes(input));\n return result.data.backupCodes;\n };\n\n // --- Account security -------------------------------------------------------\n\n const changePassword: AuthNamespace[\"changePassword\"] = async (input) => {\n const result = await call(() =>\n opts.transport.client.auth.changePassword({\n currentPassword: input.currentPassword,\n newPassword: input.newPassword,\n revokeOtherSessions: input.revokeOtherSessions,\n }),\n );\n adoptRotatedToken(result.data.accessToken);\n };\n\n const changeEmail: AuthNamespace[\"changeEmail\"] = async (input) => {\n await call(() => opts.transport.client.auth.changeEmail(input));\n };\n\n const listSessions: AuthNamespace[\"listSessions\"] = async () => {\n const result = await call(() => opts.transport.client.auth.listSessions());\n return result.data;\n };\n\n const revokeSession: AuthNamespace[\"revokeSession\"] = async (input) => {\n await call(() => opts.transport.client.auth.revokeSession(input));\n };\n\n const revokeOtherSessions: AuthNamespace[\"revokeOtherSessions\"] = async () => {\n await call(() => opts.transport.client.auth.revokeOtherSessions());\n };\n\n // The server deletes the login, so the cached bearer is dead the moment\n // this resolves — drop it locally (emits signed_out). On failure (wrong\n // password, network) the session is untouched and the error propagates.\n const closeAccount: AuthNamespace[\"closeAccount\"] = async (input) => {\n await call(() =>\n opts.transport.client.auth.closeAccount({\n confirmEmail: input.confirmEmail,\n password: input.password,\n }),\n );\n clearSession();\n };\n\n const _refresh: AuthNamespace[\"_refresh\"] = async () => {\n // Re-read storage as a fallback so a session written after\n // construction (e.g. on tab restore or in tests) is honoured.\n let current = cached;\n if (!current) {\n const fromStorage = await Promise.resolve(opts.storage.get());\n if (fromStorage) {\n cached = fromStorage;\n current = fromStorage;\n }\n }\n if (!current) {\n const err: FytkitError = { kind: \"auth_required\" };\n throw err;\n }\n try {\n const result = await opts.transport.client.auth.refresh({ token: current.accessToken });\n const { user, session } = result.data;\n return applySession(\n { accessToken: session.accessToken, expiresAt: session.expiresAt, user },\n \"session_refreshed\",\n );\n } catch (err) {\n const normalized = normalizeError(err);\n // Sign out only when the server actually rejected the token. A network\n // or server failure leaves the session in place for a later retry.\n if (isSessionRejected(normalized)) clearSession();\n throw normalized;\n }\n };\n\n const _signOutLocal: AuthNamespace[\"_signOutLocal\"] = () => clearSession();\n\n refreshFn = _refresh;\n\n return {\n signUp,\n signIn,\n signOut,\n getSession,\n onAuthStateChange,\n requestPasswordReset,\n confirmPasswordReset,\n confirmEmail,\n verifyTotp,\n verifyBackupCode,\n requestEmailOtp,\n verifyEmailOtp,\n getSecurityStatus,\n enableTotp,\n verifyTotpSetup,\n disableTwoFactor,\n regenerateBackupCodes,\n changePassword,\n changeEmail,\n listSessions,\n revokeSession,\n revokeOtherSessions,\n closeAccount,\n _refresh,\n _signOutLocal,\n };\n}\n","import { normalizeError } from \"./error\";\nimport type { Transport } from \"./transport\";\n\n/**\n * Wrap every method on a public-router slice so thrown ORPCErrors become\n * normalised FytkitErrors at the SDK boundary.\n *\n * The slice we receive is an `@orpc/client` Proxy whose own keys are not\n * enumerable — every property lookup is intercepted lazily. We mirror that\n * shape by returning a Proxy of our own that wraps each function on first\n * access and caches the result. This way the SDK never needs to know the\n * method names ahead of time, and the typed shape from the server router\n * flows through unchanged.\n */\nfunction wrapSlice<T extends object>(slice: T): T {\n const cache = new Map<string | symbol, unknown>();\n return new Proxy(slice, {\n get(target, prop, receiver) {\n if (cache.has(prop)) return cache.get(prop);\n const value = Reflect.get(target, prop, receiver);\n if (typeof value !== \"function\") {\n // Allow nested namespaces to flow through unchanged (the orpc\n // proxy returns another proxy here, not a plain object).\n return value;\n }\n const wrapped = async (...args: unknown[]) => {\n try {\n return await (value as (...a: unknown[]) => Promise<unknown>)(...args);\n } catch (err) {\n throw normalizeError(err);\n }\n };\n cache.set(prop, wrapped);\n return wrapped;\n },\n }) as T;\n}\n\nexport interface DataNamespaces {\n readonly workouts: Transport[\"client\"][\"workout\"];\n readonly exercises: Transport[\"client\"][\"exercise\"];\n readonly programs: Transport[\"client\"][\"program\"];\n readonly goals: Transport[\"client\"][\"goal\"];\n readonly equipment: Transport[\"client\"][\"equipment\"];\n readonly recipes: Transport[\"client\"][\"recipe\"];\n readonly nutrition: Transport[\"client\"][\"nutrition\"];\n readonly articles: Transport[\"client\"][\"article\"];\n readonly enrollments: Transport[\"client\"][\"enrollment\"];\n readonly favourites: Transport[\"client\"][\"favourite\"];\n readonly sessionLogs: Transport[\"client\"][\"sessionLog\"];\n readonly progression: Transport[\"client\"][\"progression\"];\n readonly bodyMetrics: Transport[\"client\"][\"bodyMetric\"];\n readonly progress: Transport[\"client\"][\"progress\"];\n readonly media: Transport[\"client\"][\"media\"];\n readonly appConfig: Transport[\"client\"][\"appConfig\"];\n readonly subscription: Transport[\"client\"][\"subscription\"];\n readonly profile: Transport[\"client\"][\"profile\"];\n readonly nutritionLog: Transport[\"client\"][\"nutritionLog\"];\n readonly preferences: Transport[\"client\"][\"preferences\"];\n}\n\nexport function createDataNamespaces(transport: Transport): DataNamespaces {\n const c = transport.client;\n return {\n workouts: wrapSlice(c.workout),\n exercises: wrapSlice(c.exercise),\n programs: wrapSlice(c.program),\n goals: wrapSlice(c.goal),\n equipment: wrapSlice(c.equipment),\n recipes: wrapSlice(c.recipe),\n nutrition: wrapSlice(c.nutrition),\n articles: wrapSlice(c.article),\n enrollments: wrapSlice(c.enrollment),\n favourites: wrapSlice(c.favourite),\n sessionLogs: wrapSlice(c.sessionLog),\n progression: wrapSlice(c.progression),\n bodyMetrics: wrapSlice(c.bodyMetric),\n progress: wrapSlice(c.progress),\n media: wrapSlice(c.media),\n appConfig: wrapSlice(c.appConfig),\n subscription: wrapSlice(c.subscription),\n profile: wrapSlice(c.profile),\n nutritionLog: wrapSlice(c.nutritionLog),\n preferences: wrapSlice(c.preferences),\n };\n}\n","import type { Session, TokenStorage } from \"./types\";\n\nconst STORAGE_KEY_PREFIX = \"fytkit:\";\nconst STORAGE_KEY_SUFFIX = \":tokens\";\n\nfunction keyFor(clientId: string): string {\n return `${STORAGE_KEY_PREFIX}${clientId}${STORAGE_KEY_SUFFIX}`;\n}\n\nfunction isValidSession(value: unknown): value is Session {\n if (!value || typeof value !== \"object\") return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.accessToken === \"string\" &&\n typeof v.expiresAt === \"string\" &&\n typeof v.user === \"object\" &&\n v.user !== null &&\n typeof (v.user as { id?: unknown }).id === \"string\" &&\n typeof (v.user as { email?: unknown }).email === \"string\"\n );\n}\n\n/**\n * Default web storage. Reads/writes JSON under `fytkit:{clientId}:tokens`.\n * Silently no-ops when localStorage throws (Safari private mode, SSR).\n */\nexport function createLocalStorageAdapter(clientId: string): TokenStorage {\n const key = keyFor(clientId);\n return {\n get: () => {\n try {\n const raw = globalThis.localStorage?.getItem(key);\n if (!raw) return null;\n const parsed: unknown = JSON.parse(raw);\n return isValidSession(parsed) ? parsed : null;\n } catch {\n return null;\n }\n },\n set: (session: Session) => {\n try {\n globalThis.localStorage?.setItem(key, JSON.stringify(session));\n } catch {\n // Quota / private-mode / SSR — swallow and let the caller treat the\n // session as in-memory-only for this tab.\n }\n },\n remove: () => {\n try {\n globalThis.localStorage?.removeItem(key);\n } catch {\n // see set() rationale\n }\n },\n };\n}\n\n/** In-memory storage. Useful for tests and SSR. */\nexport function createMemoryStorageAdapter(): TokenStorage {\n let current: Session | null = null;\n return {\n get: () => current,\n set: (session: Session) => {\n current = session;\n },\n remove: () => {\n current = null;\n },\n };\n}\n","import type { PublicApi } from \"@fytkit/types\";\nimport { createORPCClient } from \"@orpc/client\";\nimport { RPCLink } from \"@orpc/client/fetch\";\nimport type { Session } from \"./types\";\n\nexport interface CreateTransportOptions {\n readonly baseUrl: string;\n readonly clientId: string;\n readonly getSession: () => Session | null;\n readonly fetch?: typeof fetch;\n}\n\nexport interface Transport {\n readonly client: PublicApi;\n /** Internal: hook for attachRefreshInterceptor to install a fetch wrapper. */\n readonly _setFetchInterceptor: (interceptor: FetchInterceptor) => void;\n}\n\ntype FetchInterceptor = (\n request: Request,\n next: (req: Request) => Promise<Response>,\n) => Promise<Response>;\n\nexport function createTransport(options: CreateTransportOptions): Transport {\n let interceptor: FetchInterceptor | null = null;\n const baseFetch: typeof fetch = options.fetch ?? fetch.bind(globalThis);\n\n // The fetch handed to RPCLink resolves the interceptor lazily so callers\n // can install one after construction (see attachRefreshInterceptor).\n const wrappedFetch: typeof fetch = (input, init) => {\n const request = input instanceof Request ? input : new Request(input, init);\n const next = (req: Request) => baseFetch(req);\n if (!interceptor) return next(request);\n return interceptor(request, next);\n };\n\n const link = new RPCLink({\n url: () => `${options.baseUrl}/rpc`,\n headers: () => {\n const session = options.getSession();\n const headers: Record<string, string> = {\n \"X-Fytkit-Client-Id\": options.clientId,\n };\n if (session) {\n headers.Authorization = `Bearer ${session.accessToken}`;\n }\n return headers;\n },\n fetch: wrappedFetch,\n });\n\n const client: PublicApi = createORPCClient(link);\n return {\n client,\n _setFetchInterceptor: (i) => {\n interceptor = i;\n },\n };\n}\n\n/** Internal marker header — set on the refresh call so we never recurse. */\nconst REFRESH_HEADER = \"X-Fytkit-Refresh\";\n\n/**\n * Procedures that authenticate with credentials in their input (a password,\n * an emailed token, a one-time code or a sign-in challenge), never with the\n * bearer. A 401 from one of them means those credentials were rejected, not\n * that the session is dead, so it passes straight through: no refresh, no\n * replay, no local sign-out. Otherwise a member who mistypes their password\n * while signing in again (after `reauth_required`) would be signed out.\n */\nconst CREDENTIAL_PROCEDURES: readonly string[] = [\n \"auth/signIn\",\n \"auth/signUp\",\n \"auth/requestPasswordReset\",\n \"auth/confirmPasswordReset\",\n \"auth/confirmEmail\",\n \"auth/verifyTotp\",\n \"auth/verifyBackupCode\",\n \"auth/requestEmailOtp\",\n \"auth/verifyEmailOtp\",\n];\n\n/**\n * The RPCLink routes per-procedure calls under a URL whose pathname ends\n * with the procedure path (joined as `/segment/segment`), so procedures are\n * detected by suffix. endsWith keeps trailing query strings or link-level\n * prefixes from breaking detection.\n */\nfunction requestPathname(request: Request): string {\n try {\n return new URL(request.url).pathname;\n } catch {\n return \"\";\n }\n}\n\n/** Detect whether a request targets the refresh procedure. */\nfunction isRefreshRequest(request: Request): boolean {\n if (request.headers.get(REFRESH_HEADER) === \"1\") return true;\n return requestPathname(request).endsWith(\"/auth/refresh\");\n}\n\n/** Detect whether a request targets one of the `CREDENTIAL_PROCEDURES`. */\nfunction isCredentialRequest(request: Request): boolean {\n const path = requestPathname(request);\n return CREDENTIAL_PROCEDURES.some((procedure) => path.endsWith(`/${procedure}`));\n}\n\nexport interface AttachRefreshInterceptorOptions {\n readonly transport: Transport;\n readonly auth: {\n readonly _refresh: () => Promise<Session>;\n readonly _signOutLocal: () => void;\n readonly getSession: () => Session | null;\n };\n}\n\n/**\n * Install a fetch interceptor that, on a 401 response, calls auth._refresh\n * once and retries the original request. Concurrent 401s de-duplicate\n * onto a single in-flight refresh promise. A 401/403 from refresh itself or\n * a 401 from the retry triggers a local sign-out and propagates the original\n * 401 to the caller (after which orpc throws ORPCError(\"UNAUTHORIZED\")). A\n * refresh that fails for any other reason (offline, 5xx) also propagates the\n * original 401, but keeps the session so a later call can succeed. A 401 from\n * a credential procedure (`CREDENTIAL_PROCEDURES`) is never refreshed or\n * replayed: it rejects the credentials, not the session.\n */\nexport function attachRefreshInterceptor(opts: AttachRefreshInterceptorOptions): void {\n let inflight: Promise<Session> | null = null;\n\n const refreshOnce = (): Promise<Session> => {\n if (!inflight) {\n inflight = opts.auth._refresh().finally(() => {\n inflight = null;\n });\n }\n return inflight;\n };\n\n opts.transport._setFetchInterceptor(async (request, next) => {\n // Refresh calls must never trigger a recursive refresh, and a 401 from a\n // credential procedure is a wrong password or code, not a dead session.\n if (isRefreshRequest(request) || isCredentialRequest(request)) {\n return next(request);\n }\n\n // Capture the request body now, before we pass `request` to `next` —\n // once fetch consumes a Request body it can no longer be cloned.\n const buffered = await request.clone().arrayBuffer();\n\n const firstResponse = await next(request);\n if (firstResponse.status !== 401) return firstResponse;\n\n try {\n await refreshOnce();\n } catch {\n // _refresh decides whether the session survives: it signs out only\n // when the server rejected the token, not on a network/server failure.\n return firstResponse;\n }\n\n // Rebuild headers so the new Authorization header replaces the stale\n // one — the RPCLink only re-evaluates its own `headers()` once per call,\n // so we patch the bearer ourselves here.\n const replayedHeaders = new Headers(request.headers);\n const session = opts.auth.getSession();\n if (session) {\n replayedHeaders.set(\"Authorization\", `Bearer ${session.accessToken}`);\n } else {\n replayedHeaders.delete(\"Authorization\");\n }\n const replayed = new Request(request.url, {\n method: request.method,\n headers: replayedHeaders,\n body: buffered.byteLength > 0 ? buffered : undefined,\n credentials: request.credentials,\n mode: request.mode,\n cache: request.cache,\n redirect: request.redirect,\n referrer: request.referrer,\n integrity: request.integrity,\n });\n\n const retried = await next(replayed);\n if (retried.status === 401) {\n // Refresh succeeded server-side but the server still rejects — the\n // session is dead. Sign out locally and let orpc surface the 401.\n opts.auth._signOutLocal();\n }\n return retried;\n });\n}\n","import { createAuthNamespace } from \"./auth\";\nimport { createDataNamespaces } from \"./data\";\nimport { createLocalStorageAdapter, createMemoryStorageAdapter } from \"./storage\";\nimport { attachRefreshInterceptor, createTransport } from \"./transport\";\nimport type { FytkitClient, FytkitClientOptions } from \"./types\";\n\nconst DEFAULT_BASE_URL = \"https://api.fytkit.com\";\n\nexport function createFytkitClient(options: FytkitClientOptions): FytkitClient {\n if (!options || typeof options.clientId !== \"string\" || options.clientId.length === 0) {\n throw new Error(\"createFytkitClient: `clientId` is required\");\n }\n\n const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n const storage = options.storage ?? createLocalStorageAdapter(options.clientId);\n\n // `auth` is declared after `transport` because `createAuthNamespace`\n // needs the transport to make its RPC calls. The transport in turn needs\n // a `getSession` that reads the auth cache — we resolve the cycle with a\n // late-bound reference: the closure below reads `auth` lazily, which is\n // safe because no request can fire before construction finishes.\n let auth: ReturnType<typeof createAuthNamespace> | null = null;\n\n const transport = createTransport({\n baseUrl,\n clientId: options.clientId,\n getSession: () => auth?.getSession() ?? null,\n fetch: options.fetch,\n });\n\n const authInstance = createAuthNamespace({ transport, storage });\n auth = authInstance;\n attachRefreshInterceptor({ transport, auth: authInstance });\n\n const data = createDataNamespaces(transport);\n\n return {\n auth: {\n signUp: authInstance.signUp,\n signIn: authInstance.signIn,\n signOut: authInstance.signOut,\n getSession: authInstance.getSession,\n onAuthStateChange: authInstance.onAuthStateChange,\n requestPasswordReset: authInstance.requestPasswordReset,\n confirmPasswordReset: authInstance.confirmPasswordReset,\n confirmEmail: authInstance.confirmEmail,\n verifyTotp: authInstance.verifyTotp,\n verifyBackupCode: authInstance.verifyBackupCode,\n requestEmailOtp: authInstance.requestEmailOtp,\n verifyEmailOtp: authInstance.verifyEmailOtp,\n getSecurityStatus: authInstance.getSecurityStatus,\n enableTotp: authInstance.enableTotp,\n verifyTotpSetup: authInstance.verifyTotpSetup,\n disableTwoFactor: authInstance.disableTwoFactor,\n regenerateBackupCodes: authInstance.regenerateBackupCodes,\n changePassword: authInstance.changePassword,\n changeEmail: authInstance.changeEmail,\n listSessions: authInstance.listSessions,\n revokeSession: authInstance.revokeSession,\n revokeOtherSessions: authInstance.revokeOtherSessions,\n closeAccount: authInstance.closeAccount,\n },\n ...data,\n };\n}\n\n// Public re-exports.\nexport { createLocalStorageAdapter, createMemoryStorageAdapter };\n// `PublicApi` is the inferred router type that backs every namespace\n// on `FytkitClient` (e.g. `client.workouts` is `PublicApi[\"workout\"]`).\n// Re-exported so consumers can write generics over the router shape —\n// previously this surfaced as an `ae-forgotten-export` warning on\n// `FytkitClient` in the api-extractor report. The api-extractor will\n// still warn about `PublicRouter`/`publicRouter` being unexported —\n// that is intentional, those are implementation-detail names from the\n// generated snapshot that should not be part of the consumer surface.\nexport type { PublicApi } from \"@fytkit/types\";\nexport type {\n AuthState,\n FytkitClient,\n FytkitClientOptions,\n FytkitError,\n FytkitErrorCode,\n SecurityStatus,\n Session,\n SessionInfo,\n SignInResult,\n TokenStorage,\n TotpEnrollment,\n TwoFactorChallenge,\n TwoFactorMethod,\n TwoFactorToken,\n} from \"./types\";\n"]}
|