@fytkit/sdk 2.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,27 @@ 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.1.0](https://github.com/cbarlow1993/fytkit-web/compare/@fytkit/sdk@3.0.0...@fytkit/sdk@3.1.0) (2026-08-14)
8
+
9
+
10
+ ### Features
11
+
12
+ * **api:** resolve header image URLs for recipes and nutrition plans ([#188](https://github.com/cbarlow1993/fytkit-web/issues/188)) ([46bd8bd](https://github.com/cbarlow1993/fytkit-web/commit/46bd8bd8b153d4a74c32db93d1c6e171f62f6976))
13
+ * **programs:** trainer-uploadable cover image, image URLs in the SDK ([#174](https://github.com/cbarlow1993/fytkit-web/issues/174)) ([249d54e](https://github.com/cbarlow1993/fytkit-web/commit/249d54e70a5353798e01da0a87c49c5a6e8d8bd6))
14
+
15
+ ## [3.0.0](https://github.com/cbarlow1993/fytkit-web/compare/@fytkit/sdk@2.0.0...@fytkit/sdk@3.0.0) (2026-08-13)
16
+
17
+
18
+ ### ⚠ BREAKING CHANGES
19
+
20
+ * the program detail payload's `goal` enum field (strength | hypertrophy | conditioning | fat_loss | general) is removed from the public API and SDK types, replaced by `goals: Array<{ id, name }>`. The MCP updateProgramDetails tool no longer accepts `goal`; send `goalIds` (uuids from the new goals taxonomy) instead.
21
+
22
+ ### Features
23
+
24
+ * **app:** trainer-configurable Sign in with Google and Apple ([#158](https://github.com/cbarlow1993/fytkit-web/issues/158)) ([86d88dd](https://github.com/cbarlow1993/fytkit-web/commit/86d88dd419d72b32dc64dd70826c6647ecf328e9))
25
+ * **favourites:** let clients favourite content and filter lists by it ([#159](https://github.com/cbarlow1993/fytkit-web/issues/159)) ([fb9dd39](https://github.com/cbarlow1993/fytkit-web/commit/fb9dd39ed784024866b445b116fee963e4a70bd5))
26
+ * trainer-created goals for workouts and programs ([#170](https://github.com/cbarlow1993/fytkit-web/issues/170)) ([9907e37](https://github.com/cbarlow1993/fytkit-web/commit/9907e37646f69abe185ce9913520c5fcb7e03995))
27
+
7
28
  ## [2.0.0](https://github.com/cbarlow1993/fytkit-web/compare/@fytkit/sdk@1.1.0...@fytkit/sdk@2.0.0) (2026-08-02)
8
29
 
9
30
 
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ import type { RouterClient } from '@orpc/server';
6
6
  import { Schema } from '@orpc/server';
7
7
  import { ZodArray } from 'zod';
8
8
  import { ZodBoolean } from 'zod';
9
+ import { ZodCodec } from 'zod';
9
10
  import { ZodCoercedNumber } from 'zod';
10
11
  import { ZodDate } from 'zod';
11
12
  import { ZodDefault } from 'zod';
@@ -152,10 +153,12 @@ export declare interface FytkitClient {
152
153
  readonly workouts: PublicApi["workout"];
153
154
  readonly exercises: PublicApi["exercise"];
154
155
  readonly programs: PublicApi["program"];
156
+ readonly goals: PublicApi["goal"];
155
157
  readonly recipes: PublicApi["recipe"];
156
158
  readonly nutrition: PublicApi["nutrition"];
157
159
  readonly articles: PublicApi["article"];
158
160
  readonly enrollments: PublicApi["enrollment"];
161
+ readonly favourites: PublicApi["favourite"];
159
162
  readonly sessionLogs: PublicApi["sessionLog"];
160
163
  readonly progression: PublicApi["progression"];
161
164
  readonly bodyMetrics: PublicApi["bodyMetric"];
@@ -796,12 +799,14 @@ declare const publicRouter: {
796
799
  cursor: ZodOptional<ZodString>;
797
800
  limit: ZodDefault<ZodCoercedNumber<unknown>>;
798
801
  categoryId: ZodOptional<ZodString>;
802
+ goalId: ZodOptional<ZodString>;
799
803
  q: ZodOptional<ZodString>;
800
804
  difficulty: ZodOptional<ZodEnum< {
801
805
  beginner: "beginner";
802
806
  intermediate: "intermediate";
803
807
  advanced: "advanced";
804
808
  }>>;
809
+ favouritesOnly: ZodOptional<ZodUnion<readonly [ZodBoolean, ZodCodec<ZodString, ZodBoolean>]>>;
805
810
  }, $strip>, ZodObject< {
806
811
  data: ZodArray<ZodObject< {
807
812
  id: ZodString;
@@ -830,6 +835,7 @@ declare const publicRouter: {
830
835
  }>;
831
836
  videoAssetId: ZodNullable<ZodString>;
832
837
  headerImageId: ZodNullable<ZodString>;
838
+ headerImageUrl: ZodNullable<ZodString>;
833
839
  createdAt: ZodDate;
834
840
  }, $strip>>;
835
841
  meta: ZodObject< {
@@ -875,6 +881,7 @@ declare const publicRouter: {
875
881
  }>;
876
882
  videoAssetId: ZodNullable<ZodString>;
877
883
  headerImageId: ZodNullable<ZodString>;
884
+ headerImageUrl: ZodNullable<ZodString>;
878
885
  createdAt: ZodDate;
879
886
  blocks: ZodArray<ZodObject< {
880
887
  id: ZodString;
@@ -911,6 +918,10 @@ declare const publicRouter: {
911
918
  id: ZodString;
912
919
  name: ZodString;
913
920
  }, $strip>>;
921
+ goals: ZodArray<ZodObject< {
922
+ id: ZodString;
923
+ name: ZodString;
924
+ }, $strip>>;
914
925
  }, $strip>;
915
926
  }, $strip>, Record<never, never>, Record<never, never>>;
916
927
  getPlayback: DecoratedProcedure<MergedInitialContext< {
@@ -973,6 +984,7 @@ declare const publicRouter: {
973
984
  }>;
974
985
  videoAssetId: ZodNullable<ZodString>;
975
986
  headerImageId: ZodNullable<ZodString>;
987
+ headerImageUrl: ZodNullable<ZodString>;
976
988
  createdAt: ZodDate;
977
989
  }, $strip>>;
978
990
  allWorkoutsNextCursor: ZodNullable<ZodString>;
@@ -1008,6 +1020,7 @@ declare const publicRouter: {
1008
1020
  }>;
1009
1021
  videoAssetId: ZodNullable<ZodString>;
1010
1022
  headerImageId: ZodNullable<ZodString>;
1023
+ headerImageUrl: ZodNullable<ZodString>;
1011
1024
  createdAt: ZodDate;
1012
1025
  }, $strip>>;
1013
1026
  nextCursor: ZodNullable<ZodString>;
@@ -1015,6 +1028,24 @@ declare const publicRouter: {
1015
1028
  }, $strip>;
1016
1029
  }, $strip>, Record<never, never>, Record<never, never>>;
1017
1030
  };
1031
+ goal: {
1032
+ list: DecoratedProcedure<MergedInitialContext< {
1033
+ headers: Headers;
1034
+ } & Record<never, never>, {
1035
+ headers: Headers;
1036
+ } & Record<never, never>, {
1037
+ headers: Headers;
1038
+ }>, MergedCurrentContext< {
1039
+ headers: Headers;
1040
+ }, ApiKeyContext>, Schema<unknown, unknown>, ZodObject< {
1041
+ data: ZodArray<ZodObject< {
1042
+ id: ZodString;
1043
+ name: ZodString;
1044
+ displayOrder: ZodNumber;
1045
+ imageUrl: ZodNullable<ZodString>;
1046
+ }, $strip>>;
1047
+ }, $strip>, Record<never, never>, Record<never, never>>;
1048
+ };
1018
1049
  nutrition: {
1019
1050
  list: DecoratedProcedure<MergedInitialContext< {
1020
1051
  headers: Headers;
@@ -1027,6 +1058,7 @@ declare const publicRouter: {
1027
1058
  }, ApiKeyContext>, ZodObject< {
1028
1059
  cursor: ZodOptional<ZodString>;
1029
1060
  limit: ZodDefault<ZodCoercedNumber<unknown>>;
1061
+ favouritesOnly: ZodOptional<ZodUnion<readonly [ZodBoolean, ZodCodec<ZodString, ZodBoolean>]>>;
1030
1062
  }, $strip>, ZodObject< {
1031
1063
  data: ZodArray<ZodObject< {
1032
1064
  id: ZodString;
@@ -1044,6 +1076,7 @@ declare const publicRouter: {
1044
1076
  }>;
1045
1077
  publishedAt: ZodNullable<ZodDate>;
1046
1078
  headerImageId: ZodNullable<ZodString>;
1079
+ headerImageUrl: ZodNullable<ZodString>;
1047
1080
  durationWeeks: ZodNumber;
1048
1081
  householdSize: ZodNumber;
1049
1082
  tags: ZodArray<ZodString>;
@@ -1085,6 +1118,7 @@ declare const publicRouter: {
1085
1118
  }>;
1086
1119
  publishedAt: ZodNullable<ZodDate>;
1087
1120
  headerImageId: ZodNullable<ZodString>;
1121
+ headerImageUrl: ZodNullable<ZodString>;
1088
1122
  durationWeeks: ZodNumber;
1089
1123
  householdSize: ZodNumber;
1090
1124
  tags: ZodArray<ZodString>;
@@ -1105,8 +1139,8 @@ declare const publicRouter: {
1105
1139
  dayIndex: ZodNumber;
1106
1140
  kind: ZodEnum< {
1107
1141
  custom: "custom";
1108
- recipe: "recipe";
1109
1142
  note: "note";
1143
+ recipe: "recipe";
1110
1144
  }>;
1111
1145
  recipeId: ZodNullable<ZodString>;
1112
1146
  servings: ZodNumber;
@@ -1142,6 +1176,7 @@ declare const publicRouter: {
1142
1176
  }>;
1143
1177
  publishedAt: ZodNullable<ZodDate>;
1144
1178
  headerImageId: ZodNullable<ZodString>;
1179
+ headerImageUrl: ZodNullable<ZodString>;
1145
1180
  createdAt: ZodDate;
1146
1181
  }, $strip>>;
1147
1182
  }, $strip>>;
@@ -1534,6 +1569,7 @@ declare const publicRouter: {
1534
1569
  snack: "snack";
1535
1570
  }>>;
1536
1571
  q: ZodOptional<ZodString>;
1572
+ favouritesOnly: ZodOptional<ZodUnion<readonly [ZodBoolean, ZodCodec<ZodString, ZodBoolean>]>>;
1537
1573
  }, $strip>, ZodObject< {
1538
1574
  data: ZodArray<ZodObject< {
1539
1575
  id: ZodString;
@@ -1562,6 +1598,7 @@ declare const publicRouter: {
1562
1598
  }>;
1563
1599
  publishedAt: ZodNullable<ZodDate>;
1564
1600
  headerImageId: ZodNullable<ZodString>;
1601
+ headerImageUrl: ZodNullable<ZodString>;
1565
1602
  createdAt: ZodDate;
1566
1603
  }, $strip>>;
1567
1604
  meta: ZodObject< {
@@ -1607,6 +1644,7 @@ declare const publicRouter: {
1607
1644
  }>;
1608
1645
  publishedAt: ZodNullable<ZodDate>;
1609
1646
  headerImageId: ZodNullable<ZodString>;
1647
+ headerImageUrl: ZodNullable<ZodString>;
1610
1648
  createdAt: ZodDate;
1611
1649
  ingredients: ZodArray<ZodObject< {
1612
1650
  id: ZodString;
@@ -1720,6 +1758,8 @@ declare const publicRouter: {
1720
1758
  intermediate: "intermediate";
1721
1759
  advanced: "advanced";
1722
1760
  }>>;
1761
+ goalId: ZodOptional<ZodString>;
1762
+ favouritesOnly: ZodOptional<ZodUnion<readonly [ZodBoolean, ZodCodec<ZodString, ZodBoolean>]>>;
1723
1763
  }, $strip>, ZodObject< {
1724
1764
  data: ZodArray<ZodObject< {
1725
1765
  id: ZodString;
@@ -1741,6 +1781,7 @@ declare const publicRouter: {
1741
1781
  advanced: "advanced";
1742
1782
  }>>;
1743
1783
  coverImageId: ZodNullable<ZodString>;
1784
+ coverImageUrl: ZodNullable<ZodString>;
1744
1785
  publishedAt: ZodNullable<ZodString>;
1745
1786
  createdAt: ZodString;
1746
1787
  }, $strip>>;
@@ -1780,8 +1821,13 @@ declare const publicRouter: {
1780
1821
  advanced: "advanced";
1781
1822
  }>>;
1782
1823
  coverImageId: ZodNullable<ZodString>;
1824
+ coverImageUrl: ZodNullable<ZodString>;
1783
1825
  publishedAt: ZodNullable<ZodString>;
1784
1826
  createdAt: ZodString;
1827
+ goals: ZodArray<ZodObject< {
1828
+ id: ZodString;
1829
+ name: ZodString;
1830
+ }, $strip>>;
1785
1831
  goal: ZodNullable<ZodEnum< {
1786
1832
  strength: "strength";
1787
1833
  hypertrophy: "hypertrophy";
@@ -1836,6 +1882,7 @@ declare const publicRouter: {
1836
1882
  }>;
1837
1883
  videoAssetId: ZodNullable<ZodString>;
1838
1884
  headerImageId: ZodNullable<ZodString>;
1885
+ headerImageUrl: ZodNullable<ZodString>;
1839
1886
  blocks: ZodArray<ZodObject< {
1840
1887
  id: ZodString;
1841
1888
  title: ZodString;
@@ -1895,6 +1942,7 @@ declare const publicRouter: {
1895
1942
  }>;
1896
1943
  videoAssetId: ZodNullable<ZodString>;
1897
1944
  headerImageId: ZodNullable<ZodString>;
1945
+ headerImageUrl: ZodNullable<ZodString>;
1898
1946
  blocks: ZodArray<ZodObject< {
1899
1947
  id: ZodString;
1900
1948
  title: ZodString;
@@ -1967,6 +2015,7 @@ declare const publicRouter: {
1967
2015
  }>;
1968
2016
  videoAssetId: ZodNullable<ZodString>;
1969
2017
  headerImageId: ZodNullable<ZodString>;
2018
+ headerImageUrl: ZodNullable<ZodString>;
1970
2019
  blocks: ZodArray<ZodObject< {
1971
2020
  id: ZodString;
1972
2021
  title: ZodString;
@@ -2057,6 +2106,7 @@ declare const publicRouter: {
2057
2106
  advanced: "advanced";
2058
2107
  }>>;
2059
2108
  coverImageId: ZodNullable<ZodString>;
2109
+ coverImageUrl: ZodNullable<ZodString>;
2060
2110
  publishedAt: ZodNullable<ZodString>;
2061
2111
  createdAt: ZodString;
2062
2112
  }, $strip>;
@@ -2121,6 +2171,97 @@ declare const publicRouter: {
2121
2171
  };
2122
2172
  }>, Record<never, never>, Record<never, never>>;
2123
2173
  };
2174
+ favourite: {
2175
+ list: DecoratedProcedure<MergedInitialContext< {
2176
+ headers: Headers;
2177
+ } & Record<never, never>, {
2178
+ headers: Headers;
2179
+ } & Record<never, never>, {
2180
+ headers: Headers;
2181
+ }>, MergedCurrentContext< {
2182
+ headers: Headers;
2183
+ }, ApiKeyContext>, ZodObject< {
2184
+ cursor: ZodOptional<ZodString>;
2185
+ limit: ZodDefault<ZodCoercedNumber<unknown>>;
2186
+ entityType: ZodOptional<ZodEnum< {
2187
+ workout: "workout";
2188
+ program: "program";
2189
+ nutrition_plan: "nutrition_plan";
2190
+ recipe: "recipe";
2191
+ }>>;
2192
+ }, $strip>, ZodObject< {
2193
+ data: ZodArray<ZodObject< {
2194
+ entityType: ZodEnum< {
2195
+ workout: "workout";
2196
+ program: "program";
2197
+ nutrition_plan: "nutrition_plan";
2198
+ recipe: "recipe";
2199
+ }>;
2200
+ entityId: ZodString;
2201
+ favouritedAt: ZodString;
2202
+ }, $strip>>;
2203
+ meta: ZodObject< {
2204
+ nextCursor: ZodNullable<ZodString>;
2205
+ limit: ZodNumber;
2206
+ }, $strip>;
2207
+ }, $strip>, Record<never, never>, Record<never, never>>;
2208
+ add: DecoratedProcedure<MergedInitialContext< {
2209
+ headers: Headers;
2210
+ } & Record<never, never>, {
2211
+ headers: Headers;
2212
+ } & Record<never, never>, {
2213
+ headers: Headers;
2214
+ }>, MergedCurrentContext< {
2215
+ headers: Headers;
2216
+ }, ApiKeyContext>, ZodObject< {
2217
+ entityType: ZodEnum< {
2218
+ workout: "workout";
2219
+ program: "program";
2220
+ nutrition_plan: "nutrition_plan";
2221
+ recipe: "recipe";
2222
+ }>;
2223
+ entityId: ZodString;
2224
+ }, $strip>, ZodObject< {
2225
+ data: ZodObject< {
2226
+ entityType: ZodEnum< {
2227
+ workout: "workout";
2228
+ program: "program";
2229
+ nutrition_plan: "nutrition_plan";
2230
+ recipe: "recipe";
2231
+ }>;
2232
+ entityId: ZodString;
2233
+ favouritedAt: ZodString;
2234
+ }, $strip>;
2235
+ }, $strip>, Record<never, never>, Record<never, never>>;
2236
+ remove: DecoratedProcedure<MergedInitialContext< {
2237
+ headers: Headers;
2238
+ } & Record<never, never>, {
2239
+ headers: Headers;
2240
+ } & Record<never, never>, {
2241
+ headers: Headers;
2242
+ }>, MergedCurrentContext< {
2243
+ headers: Headers;
2244
+ }, ApiKeyContext>, ZodObject< {
2245
+ entityType: ZodEnum< {
2246
+ workout: "workout";
2247
+ program: "program";
2248
+ nutrition_plan: "nutrition_plan";
2249
+ recipe: "recipe";
2250
+ }>;
2251
+ entityId: ZodString;
2252
+ }, $strip>, ZodObject< {
2253
+ data: ZodObject< {
2254
+ entityType: ZodEnum< {
2255
+ workout: "workout";
2256
+ program: "program";
2257
+ nutrition_plan: "nutrition_plan";
2258
+ recipe: "recipe";
2259
+ }>;
2260
+ entityId: ZodString;
2261
+ deleted: ZodLiteral<true>;
2262
+ }, $strip>;
2263
+ }, $strip>, Record<never, never>, Record<never, never>>;
2264
+ };
2124
2265
  sessionLog: {
2125
2266
  list: DecoratedProcedure<MergedInitialContext< {
2126
2267
  headers: Headers;
@@ -2630,6 +2771,16 @@ declare const publicRouter: {
2630
2771
  helpUrl: ZodNullable<ZodString>;
2631
2772
  termsUrl: ZodNullable<ZodString>;
2632
2773
  privacyUrl: ZodNullable<ZodString>;
2774
+ auth: ZodObject< {
2775
+ googleClientId: ZodObject< {
2776
+ ios: ZodNullable<ZodString>;
2777
+ android: ZodNullable<ZodString>;
2778
+ web: ZodNullable<ZodString>;
2779
+ }, $strip>;
2780
+ apple: ZodObject< {
2781
+ servicesId: ZodNullable<ZodString>;
2782
+ }, $strip>;
2783
+ }, $strip>;
2633
2784
  }, $strip>;
2634
2785
  }, $strip>, Record<never, never>, Record<never, never>>;
2635
2786
  };
package/dist/index.js CHANGED
@@ -336,10 +336,12 @@ function createDataNamespaces(transport) {
336
336
  workouts: wrapSlice(c.workout),
337
337
  exercises: wrapSlice(c.exercise),
338
338
  programs: wrapSlice(c.program),
339
+ goals: wrapSlice(c.goal),
339
340
  recipes: wrapSlice(c.recipe),
340
341
  nutrition: wrapSlice(c.nutrition),
341
342
  articles: wrapSlice(c.article),
342
343
  enrollments: wrapSlice(c.enrollment),
344
+ favourites: wrapSlice(c.favourite),
343
345
  sessionLogs: wrapSlice(c.sessionLog),
344
346
  progression: wrapSlice(c.progression),
345
347
  bodyMetrics: wrapSlice(c.bodyMetric),
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 recipes: Transport[\"client\"][\"recipe\"];\n readonly nutrition: Transport[\"client\"][\"nutrition\"];\n readonly articles: Transport[\"client\"][\"article\"];\n readonly enrollments: Transport[\"client\"][\"enrollment\"];\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 recipes: wrapSlice(c.recipe),\n nutrition: wrapSlice(c.nutrition),\n articles: wrapSlice(c.article),\n enrollments: wrapSlice(c.enrollment),\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 * 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 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 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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fytkit/sdk",
3
- "version": "2.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Vanilla TypeScript SDK for fytkit's public API",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",