@fytkit/sdk 0.4.0 → 0.5.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,13 @@ 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
+ ## [0.5.0](https://github.com/cbarlow1993/fytkit-web/compare/@fytkit/sdk@0.4.0...@fytkit/sdk@0.5.0) (2026-07-05)
8
+
9
+
10
+ ### Features
11
+
12
+ * **sdk:** ad-hoc workout logging, stored PR history and streak endpoints ([#116](https://github.com/cbarlow1993/fytkit-web/issues/116)) ([5b069f9](https://github.com/cbarlow1993/fytkit-web/commit/5b069f993ee66d5aa4d54b04f2096d39a4ca7a37))
13
+
7
14
  ## [0.4.0](https://github.com/cbarlow1993/fytkit-web/compare/@fytkit/sdk@0.3.0...@fytkit/sdk@0.4.0) (2026-06-13)
8
15
 
9
16
 
package/README.md CHANGED
@@ -48,11 +48,42 @@ await fytkit.auth.signOut();
48
48
  off();
49
49
  ```
50
50
 
51
+ ## Tracking a workout
52
+
53
+ Log a workout ad-hoc (no program enrollment needed), record sets, complete
54
+ it, then read back progress:
55
+
56
+ ```ts
57
+ // Start an ad-hoc log against any workout — or pass { sessionId, enrollmentId }
58
+ // instead to log a scheduled program session.
59
+ const { data: log } = await fytkit.sessionLogs.create({ workoutId });
60
+
61
+ await fytkit.sessionLogs.addSet({
62
+ id: log.id,
63
+ exerciseId,
64
+ setNumber: 1,
65
+ reps: 8,
66
+ weightKg: "80.00",
67
+ });
68
+
69
+ await fytkit.sessionLogs.update({ id: log.id, completedAt: new Date().toISOString() });
70
+
71
+ // Workout-by-workout history for that workout
72
+ const { data: history } = await fytkit.sessionLogs.list({ workoutId });
73
+
74
+ // Stored progress: streak, PR history, aggregate summary
75
+ const { data: streak } = await fytkit.progress.getStreak({});
76
+ const { data: prs } = await fytkit.progression.listPrHistory({ exerciseId });
77
+ const { data: summary } = await fytkit.progress.getSummary({ period: "12w" });
78
+ ```
79
+
51
80
  ## What's included in this preview
52
81
 
53
82
  - `auth.signUp` / `signIn` / `signOut` / `getSession` / `onAuthStateChange`
54
83
  - `auth.requestPasswordReset` / `confirmPasswordReset` / `confirmEmail`
55
- - Data namespaces: `workouts`, `programs`, `recipes`, `nutrition`, `articles`, `enrollments`, `sessionLogs`, `progression`, `media`, `appConfig`, `subscription`
84
+ - Data namespaces: `workouts`, `programs`, `recipes`, `nutrition`, `nutritionLog`, `profile`, `articles`, `enrollments`, `sessionLogs`, `progression`, `progress`, `bodyMetrics`, `media`, `appConfig`, `subscription`
85
+ - Workout tracking: program session logs and ad-hoc workout logs (`sessionLogs.create` with `workoutId`), set logging, per-workout history (`sessionLogs.list({ workoutId })`)
86
+ - Progress: `progress.getSummary` / `getAdherence` / `getStreak` (stored streaks), `progression.getPrSummary` / `listPrHistory` (stored PR history) / `getHistory`, `bodyMetrics.list` / `record` / `delete`
56
87
  - Automatic header injection (`X-Fytkit-Client-Id`, `Authorization: Bearer …`)
57
88
  - Proactive refresh ~60 s before token expiry + reactive 401 retry
58
89
  - Pluggable token storage (`localStorage` default, in-memory + cookie for SSR)
package/dist/index.d.ts CHANGED
@@ -172,7 +172,7 @@ declare const publicRouter: {
172
172
  hasActiveSubscription: boolean;
173
173
  clientEntitlements: ClientEntitlements;
174
174
  authMode: "dev-app";
175
- appAllowedOrigins: string[];
175
+ appAllowedOrigins: readonly string[];
176
176
  }>, ZodObject< {
177
177
  email: ZodString;
178
178
  password: ZodString;
@@ -204,7 +204,7 @@ declare const publicRouter: {
204
204
  hasActiveSubscription: boolean;
205
205
  clientEntitlements: ClientEntitlements;
206
206
  authMode: "dev-app";
207
- appAllowedOrigins: string[];
207
+ appAllowedOrigins: readonly string[];
208
208
  }>, ZodObject< {
209
209
  email: ZodString;
210
210
  password: ZodString;
@@ -244,7 +244,7 @@ declare const publicRouter: {
244
244
  hasActiveSubscription: boolean;
245
245
  clientEntitlements: ClientEntitlements;
246
246
  authMode: "dev-app";
247
- appAllowedOrigins: string[];
247
+ appAllowedOrigins: readonly string[];
248
248
  }>, ZodObject< {
249
249
  token: ZodString;
250
250
  }, $strip>, ZodObject< {
@@ -267,7 +267,7 @@ declare const publicRouter: {
267
267
  hasActiveSubscription: boolean;
268
268
  clientEntitlements: ClientEntitlements;
269
269
  authMode: "dev-app";
270
- appAllowedOrigins: string[];
270
+ appAllowedOrigins: readonly string[];
271
271
  }>, ZodObject< {
272
272
  token: ZodString;
273
273
  }, $strip>, ZodObject< {
@@ -297,7 +297,7 @@ declare const publicRouter: {
297
297
  hasActiveSubscription: boolean;
298
298
  clientEntitlements: ClientEntitlements;
299
299
  authMode: "dev-app";
300
- appAllowedOrigins: string[];
300
+ appAllowedOrigins: readonly string[];
301
301
  }>, ZodObject< {
302
302
  email: ZodString;
303
303
  }, $strip>, ZodObject< {
@@ -320,7 +320,7 @@ declare const publicRouter: {
320
320
  hasActiveSubscription: boolean;
321
321
  clientEntitlements: ClientEntitlements;
322
322
  authMode: "dev-app";
323
- appAllowedOrigins: string[];
323
+ appAllowedOrigins: readonly string[];
324
324
  }>, ZodObject< {
325
325
  token: ZodString;
326
326
  password: ZodString;
@@ -344,7 +344,7 @@ declare const publicRouter: {
344
344
  hasActiveSubscription: boolean;
345
345
  clientEntitlements: ClientEntitlements;
346
346
  authMode: "dev-app";
347
- appAllowedOrigins: string[];
347
+ appAllowedOrigins: readonly string[];
348
348
  }>, ZodObject< {
349
349
  token: ZodString;
350
350
  }, $strip>, ZodObject< {
@@ -367,7 +367,7 @@ declare const publicRouter: {
367
367
  hasActiveSubscription: boolean;
368
368
  clientEntitlements: ClientEntitlements;
369
369
  authMode: "dev-app";
370
- appAllowedOrigins: string[];
370
+ appAllowedOrigins: readonly string[];
371
371
  }>, ZodObject< {
372
372
  password: ZodString;
373
373
  }, $strip>, ZodObject< {
@@ -391,7 +391,7 @@ declare const publicRouter: {
391
391
  hasActiveSubscription: boolean;
392
392
  clientEntitlements: ClientEntitlements;
393
393
  authMode: "dev-app";
394
- appAllowedOrigins: string[];
394
+ appAllowedOrigins: readonly string[];
395
395
  }>, ZodObject< {
396
396
  code: ZodString;
397
397
  }, $strip>, ZodObject< {
@@ -415,7 +415,7 @@ declare const publicRouter: {
415
415
  hasActiveSubscription: boolean;
416
416
  clientEntitlements: ClientEntitlements;
417
417
  authMode: "dev-app";
418
- appAllowedOrigins: string[];
418
+ appAllowedOrigins: readonly string[];
419
419
  }>, ZodObject< {
420
420
  password: ZodString;
421
421
  }, $strip>, ZodObject< {
@@ -438,7 +438,7 @@ declare const publicRouter: {
438
438
  hasActiveSubscription: boolean;
439
439
  clientEntitlements: ClientEntitlements;
440
440
  authMode: "dev-app";
441
- appAllowedOrigins: string[];
441
+ appAllowedOrigins: readonly string[];
442
442
  }>, ZodObject< {
443
443
  password: ZodString;
444
444
  }, $strip>, ZodObject< {
@@ -461,7 +461,7 @@ declare const publicRouter: {
461
461
  hasActiveSubscription: boolean;
462
462
  clientEntitlements: ClientEntitlements;
463
463
  authMode: "dev-app";
464
- appAllowedOrigins: string[];
464
+ appAllowedOrigins: readonly string[];
465
465
  }>, ZodObject< {
466
466
  token: ZodString;
467
467
  code: ZodString;
@@ -492,7 +492,7 @@ declare const publicRouter: {
492
492
  hasActiveSubscription: boolean;
493
493
  clientEntitlements: ClientEntitlements;
494
494
  authMode: "dev-app";
495
- appAllowedOrigins: string[];
495
+ appAllowedOrigins: readonly string[];
496
496
  }>, ZodObject< {
497
497
  token: ZodString;
498
498
  code: ZodString;
@@ -523,7 +523,7 @@ declare const publicRouter: {
523
523
  hasActiveSubscription: boolean;
524
524
  clientEntitlements: ClientEntitlements;
525
525
  authMode: "dev-app";
526
- appAllowedOrigins: string[];
526
+ appAllowedOrigins: readonly string[];
527
527
  }>, ZodObject< {
528
528
  token: ZodString;
529
529
  }, $strip>, ZodObject< {
@@ -546,7 +546,7 @@ declare const publicRouter: {
546
546
  hasActiveSubscription: boolean;
547
547
  clientEntitlements: ClientEntitlements;
548
548
  authMode: "dev-app";
549
- appAllowedOrigins: string[];
549
+ appAllowedOrigins: readonly string[];
550
550
  }>, ZodObject< {
551
551
  token: ZodString;
552
552
  code: ZodString;
@@ -577,7 +577,7 @@ declare const publicRouter: {
577
577
  hasActiveSubscription: boolean;
578
578
  clientEntitlements: ClientEntitlements;
579
579
  authMode: "dev-app";
580
- appAllowedOrigins: string[];
580
+ appAllowedOrigins: readonly string[];
581
581
  }>, ZodOptional<ZodObject< {}, $strip>>, ZodObject< {
582
582
  data: ZodObject< {
583
583
  twoFactorEnabled: ZodBoolean;
@@ -599,6 +599,12 @@ declare const publicRouter: {
599
599
  cursor: ZodOptional<ZodString>;
600
600
  limit: ZodDefault<ZodCoercedNumber<unknown>>;
601
601
  categoryId: ZodOptional<ZodString>;
602
+ q: ZodOptional<ZodString>;
603
+ difficulty: ZodOptional<ZodEnum< {
604
+ beginner: "beginner";
605
+ intermediate: "intermediate";
606
+ advanced: "advanced";
607
+ }>>;
602
608
  }, $strip>, ZodObject< {
603
609
  data: ZodArray<ZodObject< {
604
610
  id: ZodString;
@@ -614,6 +620,12 @@ declare const publicRouter: {
614
620
  published: "published";
615
621
  archived: "archived";
616
622
  }>;
623
+ durationMinutes: ZodNullable<ZodNumber>;
624
+ difficulty: ZodNullable<ZodEnum< {
625
+ beginner: "beginner";
626
+ intermediate: "intermediate";
627
+ advanced: "advanced";
628
+ }>>;
617
629
  publishedAt: ZodNullable<ZodDate>;
618
630
  videoAssetId: ZodNullable<ZodString>;
619
631
  headerImageId: ZodNullable<ZodString>;
@@ -649,6 +661,12 @@ declare const publicRouter: {
649
661
  published: "published";
650
662
  archived: "archived";
651
663
  }>;
664
+ durationMinutes: ZodNullable<ZodNumber>;
665
+ difficulty: ZodNullable<ZodEnum< {
666
+ beginner: "beginner";
667
+ intermediate: "intermediate";
668
+ advanced: "advanced";
669
+ }>>;
652
670
  publishedAt: ZodNullable<ZodDate>;
653
671
  videoAssetId: ZodNullable<ZodString>;
654
672
  headerImageId: ZodNullable<ZodString>;
@@ -736,6 +754,12 @@ declare const publicRouter: {
736
754
  published: "published";
737
755
  archived: "archived";
738
756
  }>;
757
+ durationMinutes: ZodNullable<ZodNumber>;
758
+ difficulty: ZodNullable<ZodEnum< {
759
+ beginner: "beginner";
760
+ intermediate: "intermediate";
761
+ advanced: "advanced";
762
+ }>>;
739
763
  publishedAt: ZodNullable<ZodDate>;
740
764
  videoAssetId: ZodNullable<ZodString>;
741
765
  headerImageId: ZodNullable<ZodString>;
@@ -761,6 +785,12 @@ declare const publicRouter: {
761
785
  published: "published";
762
786
  archived: "archived";
763
787
  }>;
788
+ durationMinutes: ZodNullable<ZodNumber>;
789
+ difficulty: ZodNullable<ZodEnum< {
790
+ beginner: "beginner";
791
+ intermediate: "intermediate";
792
+ advanced: "advanced";
793
+ }>>;
764
794
  publishedAt: ZodNullable<ZodDate>;
765
795
  videoAssetId: ZodNullable<ZodString>;
766
796
  headerImageId: ZodNullable<ZodString>;
@@ -879,6 +909,252 @@ declare const publicRouter: {
879
909
  }, $strip>;
880
910
  }, $strip>, Record<never, never>, Record<never, never>>;
881
911
  };
912
+ nutritionLog: {
913
+ getDay: DecoratedProcedure<MergedInitialContext< {
914
+ headers: Headers;
915
+ } & Record<never, never>, {
916
+ headers: Headers;
917
+ } & Record<never, never>, {
918
+ headers: Headers;
919
+ }>, MergedCurrentContext< {
920
+ headers: Headers;
921
+ }, ApiKeyContext>, ZodObject< {
922
+ date: ZodString;
923
+ }, $strip>, ZodObject< {
924
+ data: ZodObject< {
925
+ date: ZodString;
926
+ targets: ZodNullable<ZodObject< {
927
+ calories: ZodNullable<ZodCoercedNumber<unknown>>;
928
+ protein: ZodNullable<ZodCoercedNumber<unknown>>;
929
+ carbs: ZodNullable<ZodCoercedNumber<unknown>>;
930
+ fat: ZodNullable<ZodCoercedNumber<unknown>>;
931
+ }, $strip>>;
932
+ totals: ZodObject< {
933
+ calories: ZodNumber;
934
+ protein: ZodNumber;
935
+ carbs: ZodNumber;
936
+ fat: ZodNumber;
937
+ }, $strip>;
938
+ entries: ZodArray<ZodObject< {
939
+ id: ZodString;
940
+ loggedFor: ZodString;
941
+ name: ZodString;
942
+ mealCategory: ZodNullable<ZodEnum< {
943
+ breakfast: "breakfast";
944
+ lunch: "lunch";
945
+ dinner: "dinner";
946
+ snack: "snack";
947
+ }>>;
948
+ recipeId: ZodNullable<ZodString>;
949
+ calories: ZodNullable<ZodCoercedNumber<unknown>>;
950
+ protein: ZodNullable<ZodCoercedNumber<unknown>>;
951
+ carbs: ZodNullable<ZodCoercedNumber<unknown>>;
952
+ fat: ZodNullable<ZodCoercedNumber<unknown>>;
953
+ createdAt: ZodDate;
954
+ }, $strip>>;
955
+ }, $strip>;
956
+ }, $strip>, Record<never, never>, Record<never, never>>;
957
+ logFood: DecoratedProcedure<MergedInitialContext< {
958
+ headers: Headers;
959
+ } & Record<never, never>, {
960
+ headers: Headers;
961
+ } & Record<never, never>, {
962
+ headers: Headers;
963
+ }>, MergedCurrentContext< {
964
+ headers: Headers;
965
+ }, ApiKeyContext>, ZodObject< {
966
+ loggedFor: ZodString;
967
+ name: ZodString;
968
+ mealCategory: ZodOptional<ZodEnum< {
969
+ breakfast: "breakfast";
970
+ lunch: "lunch";
971
+ dinner: "dinner";
972
+ snack: "snack";
973
+ }>>;
974
+ recipeId: ZodOptional<ZodString>;
975
+ calories: ZodOptional<ZodNullable<ZodNumber>>;
976
+ protein: ZodOptional<ZodNullable<ZodNumber>>;
977
+ carbs: ZodOptional<ZodNullable<ZodNumber>>;
978
+ fat: ZodOptional<ZodNullable<ZodNumber>>;
979
+ }, $strip>, ZodObject< {
980
+ data: ZodObject< {
981
+ id: ZodString;
982
+ loggedFor: ZodString;
983
+ name: ZodString;
984
+ mealCategory: ZodNullable<ZodEnum< {
985
+ breakfast: "breakfast";
986
+ lunch: "lunch";
987
+ dinner: "dinner";
988
+ snack: "snack";
989
+ }>>;
990
+ recipeId: ZodNullable<ZodString>;
991
+ calories: ZodNullable<ZodCoercedNumber<unknown>>;
992
+ protein: ZodNullable<ZodCoercedNumber<unknown>>;
993
+ carbs: ZodNullable<ZodCoercedNumber<unknown>>;
994
+ fat: ZodNullable<ZodCoercedNumber<unknown>>;
995
+ createdAt: ZodDate;
996
+ }, $strip>;
997
+ }, $strip>, Record<never, never>, Record<never, never>>;
998
+ updateFood: DecoratedProcedure<MergedInitialContext< {
999
+ headers: Headers;
1000
+ } & Record<never, never>, {
1001
+ headers: Headers;
1002
+ } & Record<never, never>, {
1003
+ headers: Headers;
1004
+ }>, MergedCurrentContext< {
1005
+ headers: Headers;
1006
+ }, ApiKeyContext>, ZodObject< {
1007
+ name: ZodOptional<ZodString>;
1008
+ mealCategory: ZodOptional<ZodNullable<ZodEnum< {
1009
+ breakfast: "breakfast";
1010
+ lunch: "lunch";
1011
+ dinner: "dinner";
1012
+ snack: "snack";
1013
+ }>>>;
1014
+ calories: ZodOptional<ZodNullable<ZodNumber>>;
1015
+ protein: ZodOptional<ZodNullable<ZodNumber>>;
1016
+ carbs: ZodOptional<ZodNullable<ZodNumber>>;
1017
+ fat: ZodOptional<ZodNullable<ZodNumber>>;
1018
+ id: ZodString;
1019
+ }, $strip>, ZodObject< {
1020
+ data: ZodObject< {
1021
+ id: ZodString;
1022
+ loggedFor: ZodString;
1023
+ name: ZodString;
1024
+ mealCategory: ZodNullable<ZodEnum< {
1025
+ breakfast: "breakfast";
1026
+ lunch: "lunch";
1027
+ dinner: "dinner";
1028
+ snack: "snack";
1029
+ }>>;
1030
+ recipeId: ZodNullable<ZodString>;
1031
+ calories: ZodNullable<ZodCoercedNumber<unknown>>;
1032
+ protein: ZodNullable<ZodCoercedNumber<unknown>>;
1033
+ carbs: ZodNullable<ZodCoercedNumber<unknown>>;
1034
+ fat: ZodNullable<ZodCoercedNumber<unknown>>;
1035
+ createdAt: ZodDate;
1036
+ }, $strip>;
1037
+ }, $strip>, Record<never, never>, Record<never, never>>;
1038
+ deleteFood: DecoratedProcedure<MergedInitialContext< {
1039
+ headers: Headers;
1040
+ } & Record<never, never>, {
1041
+ headers: Headers;
1042
+ } & Record<never, never>, {
1043
+ headers: Headers;
1044
+ }>, MergedCurrentContext< {
1045
+ headers: Headers;
1046
+ }, ApiKeyContext>, ZodObject< {
1047
+ id: ZodString;
1048
+ }, $strip>, ZodObject< {
1049
+ data: ZodObject< {
1050
+ id: ZodString;
1051
+ deleted: ZodLiteral<true>;
1052
+ }, $strip>;
1053
+ }, $strip>, Record<never, never>, Record<never, never>>;
1054
+ setTargets: DecoratedProcedure<MergedInitialContext< {
1055
+ headers: Headers;
1056
+ } & Record<never, never>, {
1057
+ headers: Headers;
1058
+ } & Record<never, never>, {
1059
+ headers: Headers;
1060
+ }>, MergedCurrentContext< {
1061
+ headers: Headers;
1062
+ }, ApiKeyContext>, ZodObject< {
1063
+ calories: ZodOptional<ZodNullable<ZodNumber>>;
1064
+ protein: ZodOptional<ZodNullable<ZodNumber>>;
1065
+ carbs: ZodOptional<ZodNullable<ZodNumber>>;
1066
+ fat: ZodOptional<ZodNullable<ZodNumber>>;
1067
+ }, $strip>, ZodObject< {
1068
+ data: ZodNullable<ZodObject< {
1069
+ calories: ZodNullable<ZodCoercedNumber<unknown>>;
1070
+ protein: ZodNullable<ZodCoercedNumber<unknown>>;
1071
+ carbs: ZodNullable<ZodCoercedNumber<unknown>>;
1072
+ fat: ZodNullable<ZodCoercedNumber<unknown>>;
1073
+ }, $strip>>;
1074
+ }, $strip>, Record<never, never>, Record<never, never>>;
1075
+ };
1076
+ profile: {
1077
+ get: DecoratedProcedure<MergedInitialContext< {
1078
+ headers: Headers;
1079
+ } & Record<never, never>, {
1080
+ headers: Headers;
1081
+ } & Record<never, never>, {
1082
+ headers: Headers;
1083
+ }>, MergedCurrentContext< {
1084
+ headers: Headers;
1085
+ }, ApiKeyContext>, Schema<unknown, unknown>, ZodObject< {
1086
+ data: ZodObject< {
1087
+ id: ZodString;
1088
+ name: ZodString;
1089
+ email: ZodString;
1090
+ avatarUrl: ZodNullable<ZodString>;
1091
+ weightUnit: ZodEnum< {
1092
+ kg: "kg";
1093
+ lb: "lb";
1094
+ }>;
1095
+ timezone: ZodNullable<ZodString>;
1096
+ fitnessGoal: ZodNullable<ZodEnum< {
1097
+ lose_fat: "lose_fat";
1098
+ build_muscle: "build_muscle";
1099
+ get_strong: "get_strong";
1100
+ stay_consistent: "stay_consistent";
1101
+ }>>;
1102
+ macroTargets: ZodNullable<ZodObject< {
1103
+ calories: ZodNullable<ZodCoercedNumber<unknown>>;
1104
+ protein: ZodNullable<ZodCoercedNumber<unknown>>;
1105
+ carbs: ZodNullable<ZodCoercedNumber<unknown>>;
1106
+ fat: ZodNullable<ZodCoercedNumber<unknown>>;
1107
+ }, $strip>>;
1108
+ }, $strip>;
1109
+ }, $strip>, Record<never, never>, Record<never, never>>;
1110
+ update: DecoratedProcedure<MergedInitialContext< {
1111
+ headers: Headers;
1112
+ } & Record<never, never>, {
1113
+ headers: Headers;
1114
+ } & Record<never, never>, {
1115
+ headers: Headers;
1116
+ }>, MergedCurrentContext< {
1117
+ headers: Headers;
1118
+ }, ApiKeyContext>, ZodObject< {
1119
+ name: ZodOptional<ZodString>;
1120
+ avatarUrl: ZodOptional<ZodNullable<ZodString>>;
1121
+ timezone: ZodOptional<ZodNullable<ZodString>>;
1122
+ weightUnit: ZodOptional<ZodEnum< {
1123
+ kg: "kg";
1124
+ lb: "lb";
1125
+ }>>;
1126
+ fitnessGoal: ZodOptional<ZodNullable<ZodEnum< {
1127
+ lose_fat: "lose_fat";
1128
+ build_muscle: "build_muscle";
1129
+ get_strong: "get_strong";
1130
+ stay_consistent: "stay_consistent";
1131
+ }>>>;
1132
+ }, $strip>, ZodObject< {
1133
+ data: ZodObject< {
1134
+ id: ZodString;
1135
+ name: ZodString;
1136
+ email: ZodString;
1137
+ avatarUrl: ZodNullable<ZodString>;
1138
+ weightUnit: ZodEnum< {
1139
+ kg: "kg";
1140
+ lb: "lb";
1141
+ }>;
1142
+ timezone: ZodNullable<ZodString>;
1143
+ fitnessGoal: ZodNullable<ZodEnum< {
1144
+ lose_fat: "lose_fat";
1145
+ build_muscle: "build_muscle";
1146
+ get_strong: "get_strong";
1147
+ stay_consistent: "stay_consistent";
1148
+ }>>;
1149
+ macroTargets: ZodNullable<ZodObject< {
1150
+ calories: ZodNullable<ZodCoercedNumber<unknown>>;
1151
+ protein: ZodNullable<ZodCoercedNumber<unknown>>;
1152
+ carbs: ZodNullable<ZodCoercedNumber<unknown>>;
1153
+ fat: ZodNullable<ZodCoercedNumber<unknown>>;
1154
+ }, $strip>>;
1155
+ }, $strip>;
1156
+ }, $strip>, Record<never, never>, Record<never, never>>;
1157
+ };
882
1158
  article: {
883
1159
  list: DecoratedProcedure<MergedInitialContext< {
884
1160
  headers: Headers;
@@ -964,6 +1240,7 @@ declare const publicRouter: {
964
1240
  dinner: "dinner";
965
1241
  snack: "snack";
966
1242
  }>>;
1243
+ q: ZodOptional<ZodString>;
967
1244
  }, $strip>, ZodObject< {
968
1245
  data: ZodArray<ZodObject< {
969
1246
  id: ZodString;
@@ -1543,12 +1820,14 @@ declare const publicRouter: {
1543
1820
  }, ApiKeyContext>, ZodObject< {
1544
1821
  programId: ZodOptional<ZodString>;
1545
1822
  sessionId: ZodOptional<ZodString>;
1823
+ workoutId: ZodOptional<ZodString>;
1546
1824
  completed: ZodOptional<ZodBoolean>;
1547
1825
  }, $strip>, ZodObject< {
1548
1826
  data: ZodArray<ZodObject< {
1549
1827
  id: ZodString;
1550
- enrollmentId: ZodString;
1551
- sessionId: ZodString;
1828
+ enrollmentId: ZodNullable<ZodString>;
1829
+ sessionId: ZodNullable<ZodString>;
1830
+ workoutId: ZodNullable<ZodString>;
1552
1831
  startedAt: ZodString;
1553
1832
  completedAt: ZodNullable<ZodString>;
1554
1833
  overallRpe: ZodNullable<ZodNumber>;
@@ -1573,13 +1852,15 @@ declare const publicRouter: {
1573
1852
  }>, MergedCurrentContext< {
1574
1853
  headers: Headers;
1575
1854
  }, ApiKeyContext>, ZodObject< {
1576
- sessionId: ZodString;
1577
- enrollmentId: ZodString;
1855
+ sessionId: ZodOptional<ZodString>;
1856
+ enrollmentId: ZodOptional<ZodString>;
1857
+ workoutId: ZodOptional<ZodString>;
1578
1858
  }, $strip>, Schema< {
1579
1859
  data: {
1580
1860
  id: string;
1581
- enrollmentId: string;
1582
- sessionId: string;
1861
+ enrollmentId: string | null;
1862
+ sessionId: string | null;
1863
+ workoutId: string | null;
1583
1864
  startedAt: string;
1584
1865
  completedAt: string | null;
1585
1866
  overallRpe: number | null;
@@ -1589,8 +1870,9 @@ declare const publicRouter: {
1589
1870
  }, {
1590
1871
  data: {
1591
1872
  id: string;
1592
- enrollmentId: string;
1593
- sessionId: string;
1873
+ enrollmentId: string | null;
1874
+ sessionId: string | null;
1875
+ workoutId: string | null;
1594
1876
  startedAt: string;
1595
1877
  completedAt: string | null;
1596
1878
  overallRpe: number | null;
@@ -1614,8 +1896,9 @@ declare const publicRouter: {
1614
1896
  }, $strip>, Schema< {
1615
1897
  data: {
1616
1898
  id: string;
1617
- enrollmentId: string;
1618
- sessionId: string;
1899
+ enrollmentId: string | null;
1900
+ sessionId: string | null;
1901
+ workoutId: string | null;
1619
1902
  startedAt: string;
1620
1903
  completedAt: string | null;
1621
1904
  overallRpe: number | null;
@@ -1633,8 +1916,9 @@ declare const publicRouter: {
1633
1916
  }, {
1634
1917
  data: {
1635
1918
  id: string;
1636
- enrollmentId: string;
1637
- sessionId: string;
1919
+ enrollmentId: string | null;
1920
+ sessionId: string | null;
1921
+ workoutId: string | null;
1638
1922
  startedAt: string;
1639
1923
  completedAt: string | null;
1640
1924
  overallRpe: number | null;
@@ -1809,6 +2093,29 @@ declare const publicRouter: {
1809
2093
  computedAt: ZodString;
1810
2094
  }, $strip>;
1811
2095
  }, $strip>, Record<never, never>, Record<never, never>>;
2096
+ listPrHistory: DecoratedProcedure<MergedInitialContext< {
2097
+ headers: Headers;
2098
+ } & Record<never, never>, {
2099
+ headers: Headers;
2100
+ } & Record<never, never>, {
2101
+ headers: Headers;
2102
+ }>, MergedCurrentContext< {
2103
+ headers: Headers;
2104
+ }, ApiKeyContext>, ZodObject< {
2105
+ exerciseId: ZodOptional<ZodString>;
2106
+ limit: ZodDefault<ZodCoercedNumber<unknown>>;
2107
+ }, $strip>, ZodObject< {
2108
+ data: ZodArray<ZodObject< {
2109
+ id: ZodString;
2110
+ exerciseId: ZodString;
2111
+ exerciseName: ZodString;
2112
+ sessionLogId: ZodString;
2113
+ weightKg: ZodString;
2114
+ reps: ZodNumber;
2115
+ estimated1Rm: ZodString;
2116
+ achievedAt: ZodString;
2117
+ }, $strip>>;
2118
+ }, $strip>, Record<never, never>, Record<never, never>>;
1812
2119
  };
1813
2120
  bodyMetric: {
1814
2121
  list: DecoratedProcedure<MergedInitialContext< {
@@ -1936,6 +2243,21 @@ declare const publicRouter: {
1936
2243
  }, $strip>>;
1937
2244
  }, $strip>;
1938
2245
  }, $strip>, Record<never, never>, Record<never, never>>;
2246
+ getStreak: DecoratedProcedure<MergedInitialContext< {
2247
+ headers: Headers;
2248
+ } & Record<never, never>, {
2249
+ headers: Headers;
2250
+ } & Record<never, never>, {
2251
+ headers: Headers;
2252
+ }>, MergedCurrentContext< {
2253
+ headers: Headers;
2254
+ }, ApiKeyContext>, ZodObject< {}, $strip>, ZodObject< {
2255
+ data: ZodObject< {
2256
+ currentStreakDays: ZodNumber;
2257
+ longestStreakDays: ZodNumber;
2258
+ lastWorkoutAt: ZodNullable<ZodString>;
2259
+ }, $strip>;
2260
+ }, $strip>, Record<never, never>, Record<never, never>>;
1939
2261
  };
1940
2262
  media: {
1941
2263
  get: DecoratedProcedure<MergedInitialContext< {
@@ -2011,6 +2333,9 @@ declare const publicRouter: {
2011
2333
  id: ZodString;
2012
2334
  name: ZodString;
2013
2335
  monthlyPriceCents: ZodNumber;
2336
+ annualPriceCents: ZodNullable<ZodNumber>;
2337
+ hasAnnualStripe: ZodBoolean;
2338
+ trialDays: ZodNullable<ZodNumber>;
2014
2339
  description: ZodString;
2015
2340
  sortOrder: ZodNumber;
2016
2341
  hasStripe: ZodBoolean;
@@ -2073,6 +2398,10 @@ declare const publicRouter: {
2073
2398
  headers: Headers;
2074
2399
  }, ApiKeyContext>, ZodObject< {
2075
2400
  planId: ZodString;
2401
+ interval: ZodDefault<ZodEnum< {
2402
+ monthly: "monthly";
2403
+ annual: "annual";
2404
+ }>>;
2076
2405
  returnUrl: ZodOptional<ZodString>;
2077
2406
  }, $strip>, ZodObject< {
2078
2407
  data: ZodObject< {
package/dist/index.js CHANGED
@@ -246,7 +246,9 @@ function createDataNamespaces(transport) {
246
246
  progress: wrapSlice(c.progress),
247
247
  media: wrapSlice(c.media),
248
248
  appConfig: wrapSlice(c.appConfig),
249
- subscription: wrapSlice(c.subscription)
249
+ subscription: wrapSlice(c.subscription),
250
+ profile: wrapSlice(c.profile),
251
+ nutritionLog: wrapSlice(c.nutritionLog)
250
252
  };
251
253
  }
252
254
 
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 \"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 { AuthState, FytkitError, Session, SignInResult, TokenStorage, TwoFactorToken } 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 /** 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 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 _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 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}\n\nexport function createDataNamespaces(transport: Transport): DataNamespaces {\n const c = transport.client;\n return {\n workouts: wrapSlice(c.workout),\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 };\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 },\n ...data,\n };\n}\n\n// Public re-exports.\nexport { createLocalStorageAdapter, createMemoryStorageAdapter };\nexport type {\n AuthState,\n FytkitClient,\n FytkitClientOptions,\n FytkitError,\n Session,\n SignInResult,\n TokenStorage,\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 \"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 { AuthState, FytkitError, Session, SignInResult, TokenStorage, TwoFactorToken } 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 /** 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 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 _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 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}\n\nexport function createDataNamespaces(transport: Transport): DataNamespaces {\n const c = transport.client;\n return {\n workouts: wrapSlice(c.workout),\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 };\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 },\n ...data,\n };\n}\n\n// Public re-exports.\nexport { createLocalStorageAdapter, createMemoryStorageAdapter };\nexport type {\n AuthState,\n FytkitClient,\n FytkitClientOptions,\n FytkitError,\n Session,\n SignInResult,\n TokenStorage,\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": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Vanilla TypeScript SDK for fytkit's public API",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",