@fytkit/sdk 3.4.0 → 3.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 +7 -0
- package/README.md +81 -4
- package/dist/index.d.ts +1737 -39
- package/dist/index.js +358 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
+
## [3.5.0](https://github.com/cbarlow1993/fytkit-web/compare/@fytkit/sdk@3.4.0...@fytkit/sdk@3.5.0) (2026-10-10)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* member coaching loop ([#461](https://github.com/cbarlow1993/fytkit-web/issues/461)) [integration branch] ([#482](https://github.com/cbarlow1993/fytkit-web/issues/482)) ([75291c1](https://github.com/cbarlow1993/fytkit-web/commit/75291c1ba29b142b96fe3452b61a25e37f016c8b))
|
|
13
|
+
|
|
7
14
|
## [3.4.0](https://github.com/cbarlow1993/fytkit-web/compare/@fytkit/sdk@3.3.0...@fytkit/sdk@3.4.0) (2026-10-04)
|
|
8
15
|
|
|
9
16
|
|
package/README.md
CHANGED
|
@@ -117,17 +117,20 @@ await fytkit.auth.regenerateBackupCodes({ password });
|
|
|
117
117
|
await fytkit.auth.disableTwoFactor({ password });
|
|
118
118
|
|
|
119
119
|
// Password change (verifies the current password server-side). With
|
|
120
|
-
// revokeOtherSessions
|
|
120
|
+
// revokeOtherSessions every other session of the account ends, in any app,
|
|
121
|
+
// and the SDK adopts the rotated token transparently.
|
|
121
122
|
await fytkit.auth.changePassword({ currentPassword, newPassword, revokeOtherSessions: true });
|
|
122
123
|
|
|
123
124
|
// Email change — a confirmation link is emailed to the new address;
|
|
124
125
|
// nothing changes until the trainee opens it.
|
|
125
126
|
await fytkit.auth.changeEmail({ newEmail });
|
|
126
127
|
|
|
127
|
-
// Devices
|
|
128
|
+
// Devices signed in through this app (plus the calling one); the trainee's
|
|
129
|
+
// sessions elsewhere are never listed. revokeId is an opaque id (never the
|
|
130
|
+
// bearer token).
|
|
128
131
|
const sessions = await fytkit.auth.listSessions();
|
|
129
132
|
await fytkit.auth.revokeSession({ revokeId: sessions.find((s) => !s.isCurrent)!.revokeId });
|
|
130
|
-
await fytkit.auth.revokeOtherSessions(); // "sign out of
|
|
133
|
+
await fytkit.auth.revokeOtherSessions(); // "sign out of your other devices" on this app
|
|
131
134
|
|
|
132
135
|
// Permanently close the account (re-verifies email + password). On success the
|
|
133
136
|
// SDK clears the session and emits signed_out.
|
|
@@ -143,6 +146,76 @@ after `reauth_required`) is a plain `auth_required` and leaves the current
|
|
|
143
146
|
session alone. Enrolling twice in a program is `{ kind: "conflict" }` with the
|
|
144
147
|
existing `enrollmentId`.
|
|
145
148
|
|
|
149
|
+
## Refusals
|
|
150
|
+
|
|
151
|
+
When the server says no for a reason your app can act on (a coaching feature
|
|
152
|
+
the plan lacks, a check-in already sent, a messaging limit, a studio that is
|
|
153
|
+
full), the error carries `refusal: { reason, data }` beside its usual fields.
|
|
154
|
+
Branch on `refusal.reason`, never on message text; `refusalData` reads a
|
|
155
|
+
known reason's data typed:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import { refusalData } from "@fytkit/sdk";
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
await fytkit.messages.send({ body, clientRequestId });
|
|
162
|
+
} catch (err) {
|
|
163
|
+
const plan = refusalData(err, "entitlement_required");
|
|
164
|
+
if (plan) return showUpgrade(plan.entitlement);
|
|
165
|
+
const daily = refusalData(err, "daily_limit");
|
|
166
|
+
if (daily) return keepDraftUntil(daily.retryAt);
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Every reason and its data: [`docs/developer/reference/errors.md`](../../docs/developer/reference/errors.md#refusals).
|
|
172
|
+
|
|
173
|
+
## Live chat
|
|
174
|
+
|
|
175
|
+
`subscribeToMessages` keeps a conversation screen current over Supabase
|
|
176
|
+
Realtime: it fetches a token (`messages.realtimeToken`) and fytkit's public
|
|
177
|
+
connection settings (`appConfig.get`), joins the trainee's own private
|
|
178
|
+
channel (and joins it afresh after Realtime closes it), renews the token
|
|
179
|
+
before it expires, and reports when to poll instead. Events carry no message content: `onChange` means "read the
|
|
180
|
+
conversation and the unread count again".
|
|
181
|
+
|
|
182
|
+
The SDK has no dependency on supabase-js. Add `@supabase/supabase-js` to
|
|
183
|
+
your app (`npm install @supabase/supabase-js@^2.97.0`) and pass a factory
|
|
184
|
+
that creates a client for the subscription alone. It must be 2.87.1 or
|
|
185
|
+
later: the helper sets the trainee's token on the socket by hand, and
|
|
186
|
+
earlier 2.x clients swap it for the anon key on the next heartbeat, which
|
|
187
|
+
leaves the subscription in `fallback`. Keep the `auth` options below: the
|
|
188
|
+
client carries the trainee's token only, and must not keep or refresh a
|
|
189
|
+
session, or read a sign-in from your page's URL that would replace it.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { createClient } from "@supabase/supabase-js";
|
|
193
|
+
import { MESSAGES_FALLBACK_POLL_MS, subscribeToMessages } from "@fytkit/sdk";
|
|
194
|
+
|
|
195
|
+
const unsubscribe = subscribeToMessages(fytkit, {
|
|
196
|
+
realtime: ({ url, anonKey }) =>
|
|
197
|
+
createClient(url, anonKey, {
|
|
198
|
+
auth: { persistSession: false, autoRefreshToken: false, detectSessionInUrl: false },
|
|
199
|
+
}),
|
|
200
|
+
onChange: () => refreshConversation(), // messages.list + messages.unreadCount
|
|
201
|
+
onStatus: (status) => {
|
|
202
|
+
// "live": stop polling. "connecting" / "fallback": poll the conversation
|
|
203
|
+
// every MESSAGES_FALLBACK_POLL_MS.conversation (10 s) and the count every
|
|
204
|
+
// MESSAGES_FALLBACK_POLL_MS.unreadCount (60 s). "refused": no trainee to
|
|
205
|
+
// listen for (signed out); the subscription has stopped.
|
|
206
|
+
setPolling(status === "connecting" || status === "fallback");
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// On leaving the screen or signing out:
|
|
211
|
+
unsubscribe();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Polling spends your app's per-minute request limit, which all its users
|
|
215
|
+
share, so stop as soon as the status is `live`. The full guide:
|
|
216
|
+
[`messages.realtimeToken`](../../docs/developer/reference/data-namespaces.md#messagesrealtimetoken)
|
|
217
|
+
and the recipe [Chat screen with live updates](../../docs/developer/recipes/chat-screen-with-live-updates.md).
|
|
218
|
+
|
|
146
219
|
## Notification preferences
|
|
147
220
|
|
|
148
221
|
Per-member toggles stored server-side (defaults apply until first write):
|
|
@@ -158,9 +231,13 @@ await fytkit.preferences.updateNotifications({ streakAlerts: false }); // partia
|
|
|
158
231
|
- `auth.requestPasswordReset` / `confirmPasswordReset` / `confirmEmail`
|
|
159
232
|
- Two-factor: `auth.verifyTotp` / `verifyBackupCode` / `requestEmailOtp` / `verifyEmailOtp` (sign-in), `enableTotp` / `verifyTotpSetup` / `disableTwoFactor` / `regenerateBackupCodes` / `getSecurityStatus` (management)
|
|
160
233
|
- Account security: `auth.changePassword` / `changeEmail` / `listSessions` / `revokeSession` / `revokeOtherSessions` / `closeAccount`
|
|
161
|
-
- Data namespaces: `workouts`, `programs`, `recipes`, `nutrition`, `nutritionLog`, `profile`, `preferences`, `articles`, `enrollments`, `sessionLogs`, `progression`, `progress`, `bodyMetrics`, `media`, `appConfig`, `subscription`
|
|
234
|
+
- Data namespaces: `workouts`, `programs`, `recipes`, `nutrition`, `nutritionLog`, `profile`, `preferences`, `articles`, `enrollments`, `sessionLogs`, `progression`, `progress`, `bodyMetrics`, `checkIns`, `messages`, `media`, `appConfig`, `subscription`
|
|
162
235
|
- Workout tracking: program session logs and ad-hoc workout logs (`sessionLogs.create` with `workoutId`), set logging, per-workout history (`sessionLogs.list({ workoutId })`)
|
|
163
236
|
- Progress: `progress.getSummary` / `getAdherence` / `getStreak` (stored streaks), `progression.getPrSummary` / `listPrHistory` (stored PR history) / `getHistory`, `bodyMetrics.list` / `record` / `delete`
|
|
237
|
+
- Coach feedback on logged sessions: `sessionLogs.getFeedback` / `markFeedbackSeen` / `unseenFeedbackCount`
|
|
238
|
+
- Check-ins: `checkIns.current` / `submit` / `update` / `list` / `get` / `markReplySeen` / `requestPhotoUpload` (weekly or fortnightly check-ins a coach reviews; needs a plan with `coaching_checkins`)
|
|
239
|
+
- Messages: `messages.list` / `send` / `requestAttachmentUpload` / `markRead` / `delete` / `unreadCount` / `realtimeToken` (the trainee's conversation with their coaches; starting one needs a plan with `coaching_messages`, answering a coach does not; `requestAttachmentUpload` reserves a photo to send with a message)
|
|
240
|
+
- Live chat: `subscribeToMessages` (joins the trainee's private Realtime channel with `messages.realtimeToken`, renews the token, and says when to poll instead; see [Live chat](#live-chat))
|
|
164
241
|
- Automatic header injection (`X-Fytkit-Client-Id`, `Authorization: Bearer …`)
|
|
165
242
|
- Proactive refresh ~60 s before token expiry (retried with backoff when offline) + reactive 401 retry; only a server-rejected refresh signs out
|
|
166
243
|
- Pluggable token storage (`localStorage` default, in-memory + cookie for SSR)
|