@fytkit/sdk 3.5.0 → 4.0.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 +23 -0
- package/README.md +46 -5
- package/dist/index.d.ts +2515 -944
- package/dist/index.js +122 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ 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
|
+
## 4.0.0
|
|
8
|
+
|
|
9
|
+
To be published by hand (release-please, which runs on GitHub Actions, does not cut this release; the manifest records 4.0.0 so the next automated release bumps from it). Create the tag and GitHub release `@fytkit/sdk@4.0.0` when it is published, or release-please proposes 4.0.0 again.
|
|
10
|
+
|
|
11
|
+
### ⚠ BREAKING CHANGES
|
|
12
|
+
|
|
13
|
+
* `appConfig.get` is removed, with no deprecation window: the `appConfig` namespace is gone from `FytkitClient`, and `GET /api/v1/app-config` and `POST /rpc/appConfig/get` answer 404. Use `app.bootstrap` instead. Its colours become the app theme's curated palette (`primaryColor` → `data.theme.palette.primary`, and so on), `appName` → `data.studio.name`, the links → `data.links`, `realtime` → `data.realtime`; the icon URLs, `buildStatus`, `buildVersion`, the social sign-in ids (`auth`), `id` and `trainerId` are gone. Field-by-field migration: [docs/developer/reference/app-bootstrap.md](https://github.com/cbarlow1993/fytkit-web/blob/main/docs/developer/reference/app-bootstrap.md#migrating-from-appconfigget) ([#535](https://github.com/cbarlow1993/fytkit-web/issues/535))
|
|
14
|
+
* `MessagesRealtimeSource` reads the Realtime settings from `app.bootstrap()` (`{ status: "ok", data: { realtime } }`) instead of `appConfig.get()`. A `FytkitClient` still satisfies it; a hand-written source must rename `appConfig.get` to `app.bootstrap` ([#535](https://github.com/cbarlow1993/fytkit-web/issues/535))
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* `app.bootstrap({ etag? })`: the app's launch read. The studio's display name and logo, the effective app theme (template, theme, palette, derived variants and the oldest app release that paints it), every template's minimum app version, feature overrides, the studio's minimum app version, the terms, privacy and help links, the Realtime settings and the payment policy, in one request that needs the client id alone. Resolves to `{ status: "ok", data, etag }`, or to `{ status: "unchanged", etag }` when the `etag` you stored is still current (the server answers 304 with no body). New types `AppBootstrapData`, `AppBootstrapOptions`, `AppBootstrapResult` and `AppNamespace`; `FytkitClient.clientId` names the client id the client was created with, to key per-studio caches by ([#535](https://github.com/cbarlow1993/fytkit-web/issues/535))
|
|
19
|
+
* `app.bootstrap`'s `data.paymentPolicy` and `resolvePaymentMode(policy, { platform, storefront, conditionsMet? })`: whether the app shows "Subscribe" (a link out to the studio's checkout) or "What your plan includes" for the storefront the device's store reports (StoreKit's alpha-3 `Storefront.countryCode` on iOS, Play Billing's alpha-2 country code on Android; case-insensitive). The policy is Fytkit's reviewed list of storefronts where linking out is allowed, with any conditions the app must meet first; every other storefront, and any unmet or unknown condition, is plan info. New types `PaymentMode`, `PaymentPlatform`, `PaymentPolicy` and `ResolvePaymentModeOptions` ([#538](https://github.com/cbarlow1993/fytkit-web/issues/538))
|
|
20
|
+
* `resolveJoinCode(code, { baseUrl?, fetch? })`: resolves a studio's join code (the code on its Fytkit Coach QR code or join link) to the studio's client id, name, logo URL and app theme, with no client id or session. An unknown or replaced code rejects with `{ kind: "not_found", refusal: { reason: "join_code_inactive" } }`; new types `JoinCodeResolution`, `JoinCodeTheme` and `ResolveJoinCodeOptions` ([#529](https://github.com/cbarlow1993/fytkit-web/issues/529))
|
|
21
|
+
* `resolveJoinCode`'s `studio` gains `coaches` (at most 3; today the studio's own public coach profile alone: `name` and the public profile image as `avatarUrl`, null when there is none; never staff names or emails) and `programCount` (published programmes, any access level; drafts and archived ones never count), for a welcome screen's "Coached by" row and programme count. Additive. New type `JoinCodeCoach` ([#600](https://github.com/cbarlow1993/fytkit-web/issues/600))
|
|
22
|
+
* `FytkitRefusalReason` gains `join_code_inactive` ([#529](https://github.com/cbarlow1993/fytkit-web/issues/529))
|
|
23
|
+
* `auth.resendVerification()`: mails a new email-verification link to the signed-in member's own address (a session is required; there is no email input), for a "Send it again" link on a confirm-your-email screen. Resolves to `{ status: "sent" }`, or to `{ status: "already_verified" }` with nothing sent when the address is verified already. Throttled per account (one send a minute, five an hour): over the limit it rejects with `{ kind: "rate_limited" }`. Fytkit Coach members get the app-link URL, as at sign-up. A bearer call, so a 401 refreshes or signs out as usual. Additive. New type `ResendVerificationResult` ([#615](https://github.com/cbarlow1993/fytkit-web/issues/615))
|
|
24
|
+
* `auth.confirmEmailChange({ token })`: applies an email change from the token in the confirmation link, for an app that opens that link itself (Fytkit Coach's app link). Needs no session; idempotent while the address still holds ([#532](https://github.com/cbarlow1993/fytkit-web/issues/532))
|
|
25
|
+
* `sessionLogs.create` and `sessionLogs.addSet` take an optional `clientRequestId` (a UUID) so an offline queue or a retry can replay a write safely: a replay with the same key returns the stored log or set instead of creating a duplicate log or failing the set with `conflict`, and a key reused for another log or set is `conflict`. On a replayed `create`, `sets` holds the log's current sets, so its type is now the set array instead of `never[]`. Calls without a key are unchanged ([#603](https://github.com/cbarlow1993/fytkit-web/issues/603))
|
|
26
|
+
* `bodyMetrics.record` and `nutritionLog.logFood` take an optional `clientRequestId` (a UUID) for the same offline replay: a replay with the same key returns the stored weigh-in (with the warning `update` would give it) or food log entry, with their current values, instead of recording a second one, and writes no second timeline event. A `logFood` key reused for another `loggedFor` day is `conflict`; a weigh-in's values can all be edited, so a `record` replay returns the stored weigh-in whatever it sends. Keys are scoped to the trainee. `nutritionLog.updateFood` and `nutritionLog.setTargets` take no key: they set absolute values, so a replay is already safe. Calls without a key are unchanged (W17, W18)
|
|
27
|
+
* `sessionLogs.delete({ id })`: deletes one of the member's own session logs, in progress or finished, with its sets, personal records and any staff review, comments and reactions; its timeline events go too, and the streak, personal records and program enrollment completion are recomputed. Resolves to `{ data: { id, deleted: true } }`; another member's log or an unknown id is `not_found`, and the call is refused while a member is impersonated ([#602](https://github.com/cbarlow1993/fytkit-web/issues/602))
|
|
28
|
+
* member coaching loop Phase 2: the `bodyMeasurements`, `progressPhotos`, `timeline`, `health` and `habits` namespaces, `progress.getVolume`, `progression.getE1rmTrend`, `bodyMetrics.update`, and the refusal reasons `staff_entered`, `checkin_sourced`, `consent_required`, `consent_outdated`, `too_many_pending`, `photo_limit_reached`, `invalid_uploads` and `poses_differ`, with the `coaching_habits` entitlement refusal and the `first_party_account` error code ([#461](https://github.com/cbarlow1993/fytkit-web/issues/461)) ([#487](https://github.com/cbarlow1993/fytkit-web/issues/487))
|
|
29
|
+
|
|
7
30
|
## [3.5.0](https://github.com/cbarlow1993/fytkit-web/compare/@fytkit/sdk@3.4.0...@fytkit/sdk@3.5.0) (2026-10-10)
|
|
8
31
|
|
|
9
32
|
|
package/README.md
CHANGED
|
@@ -48,6 +48,42 @@ await fytkit.auth.signOut();
|
|
|
48
48
|
off();
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
## Joining a studio and launching
|
|
52
|
+
|
|
53
|
+
A Fytkit Coach style app starts from a studio's join code (its QR code or
|
|
54
|
+
join link) and reads the studio's launch settings once per launch:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { createFytkitClient, resolveJoinCode, resolvePaymentMode } from "@fytkit/sdk";
|
|
58
|
+
|
|
59
|
+
// Once, when the member scans or types the code: keep the client id.
|
|
60
|
+
const studio = await resolveJoinCode(scannedCode);
|
|
61
|
+
const fytkit = createFytkitClient({ clientId: studio.clientId });
|
|
62
|
+
|
|
63
|
+
// Every launch: revalidate the stored settings with their ETag.
|
|
64
|
+
const stored = await loadStoredBootstrap(); // { data, etag } | null, your storage
|
|
65
|
+
const result = await fytkit.app.bootstrap({ etag: stored?.etag });
|
|
66
|
+
if (result.status === "ok") await saveStoredBootstrap({ data: result.data, etag: result.etag });
|
|
67
|
+
const settings = result.status === "ok" ? result.data : stored?.data;
|
|
68
|
+
// settings.studio (name, logoUrl), settings.theme (palette, variants),
|
|
69
|
+
// settings.featureOverrides, settings.minAppVersion, settings.links,
|
|
70
|
+
// settings.realtime, settings.paymentPolicy
|
|
71
|
+
|
|
72
|
+
// "Subscribe" (a link out to the studio's checkout) only where the store allows it.
|
|
73
|
+
const { mode } = resolvePaymentMode(settings.paymentPolicy, {
|
|
74
|
+
platform: "ios",
|
|
75
|
+
storefront: storefrontCountryCode, // StoreKit's Storefront.current?.countryCode
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`app.bootstrap` resolves to `{ status: "unchanged" }` (a 304, no body) while
|
|
80
|
+
the `etag` you sent is current. It replaced `appConfig.get` in 4.0.0:
|
|
81
|
+
[`app.bootstrap`](../../docs/developer/reference/app-bootstrap.md), with the
|
|
82
|
+
field-by-field migration. [`resolveJoinCode`](../../docs/developer/reference/resolveJoinCode.md)
|
|
83
|
+
needs no client. `resolvePaymentMode` is plan info ("What your plan includes")
|
|
84
|
+
for every storefront the policy doesn't list:
|
|
85
|
+
[Subscribe or what your plan includes](../../docs/developer/reference/app-bootstrap.md#subscribe-or-what-your-plan-includes).
|
|
86
|
+
|
|
51
87
|
## Tracking a workout
|
|
52
88
|
|
|
53
89
|
Log a workout ad-hoc (no program enrollment needed), record sets, complete
|
|
@@ -143,7 +179,11 @@ fresh sign-in is `{ kind: "forbidden", code: "reauth_required" }`. None of
|
|
|
143
179
|
these sign the trainee out — only a 401 on an authenticated call means the
|
|
144
180
|
session is gone. A wrong password on `signIn` (for example, signing in again
|
|
145
181
|
after `reauth_required`) is a plain `auth_required` and leaves the current
|
|
146
|
-
session alone.
|
|
182
|
+
session alone. An account that also runs or works at a trainer on Fytkit can't
|
|
183
|
+
sign in, sign up or complete two-factor through an app, and an existing app
|
|
184
|
+
session can't change its password or email or close it:
|
|
185
|
+
`{ kind: "forbidden", code: "first_party_account" }`; point it to Fytkit's own
|
|
186
|
+
sign-in. Enrolling twice in a program is `{ kind: "conflict" }` with the
|
|
147
187
|
existing `enrollmentId`.
|
|
148
188
|
|
|
149
189
|
## Refusals
|
|
@@ -174,7 +214,7 @@ Every reason and its data: [`docs/developer/reference/errors.md`](../../docs/dev
|
|
|
174
214
|
|
|
175
215
|
`subscribeToMessages` keeps a conversation screen current over Supabase
|
|
176
216
|
Realtime: it fetches a token (`messages.realtimeToken`) and fytkit's public
|
|
177
|
-
connection settings (`
|
|
217
|
+
connection settings (`app.bootstrap`), joins the trainee's own private
|
|
178
218
|
channel (and joins it afresh after Realtime closes it), renews the token
|
|
179
219
|
before it expires, and reports when to poll instead. Events carry no message content: `onChange` means "read the
|
|
180
220
|
conversation and the unread count again".
|
|
@@ -227,13 +267,14 @@ await fytkit.preferences.updateNotifications({ streakAlerts: false }); // partia
|
|
|
227
267
|
|
|
228
268
|
## What's included in this preview
|
|
229
269
|
|
|
270
|
+
- `resolveJoinCode` (a studio's join code to its client id, name, logo and app theme, with no client) and `app.bootstrap` (the launch read, revalidated with its ETag), with `resolvePaymentMode` ("Subscribe" link-out or "What your plan includes", per storefront)
|
|
230
271
|
- `auth.signUp` / `signIn` / `signOut` / `getSession` / `onAuthStateChange`
|
|
231
272
|
- `auth.requestPasswordReset` / `confirmPasswordReset` / `confirmEmail`
|
|
232
273
|
- Two-factor: `auth.verifyTotp` / `verifyBackupCode` / `requestEmailOtp` / `verifyEmailOtp` (sign-in), `enableTotp` / `verifyTotpSetup` / `disableTwoFactor` / `regenerateBackupCodes` / `getSecurityStatus` (management)
|
|
233
|
-
- Account security: `auth.changePassword` / `changeEmail` / `listSessions` / `revokeSession` / `revokeOtherSessions` / `closeAccount`
|
|
234
|
-
- Data namespaces: `workouts`, `programs`, `recipes`, `nutrition`, `nutritionLog`, `profile`, `preferences`, `articles`, `enrollments`, `sessionLogs`, `progression`, `progress`, `bodyMetrics`, `checkIns`, `messages`, `
|
|
274
|
+
- Account security: `auth.changePassword` / `changeEmail` / `confirmEmailChange` / `listSessions` / `revokeSession` / `revokeOtherSessions` / `closeAccount`
|
|
275
|
+
- Data namespaces: `workouts`, `programs`, `recipes`, `nutrition`, `nutritionLog`, `profile`, `preferences`, `articles`, `enrollments`, `sessionLogs`, `progression`, `progress`, `bodyMetrics`, `bodyMeasurements`, `progressPhotos`, `checkIns`, `messages`, `timeline`, `health`, `habits`, `media`, `subscription`
|
|
235
276
|
- Workout tracking: program session logs and ad-hoc workout logs (`sessionLogs.create` with `workoutId`), set logging, per-workout history (`sessionLogs.list({ workoutId })`)
|
|
236
|
-
- Progress: `progress.getSummary` / `getAdherence` / `getStreak` (stored streaks), `progression.getPrSummary` / `listPrHistory` (stored PR history) / `getHistory`, `bodyMetrics.list` / `record` / `delete`
|
|
277
|
+
- Progress: `progress.getSummary` / `getAdherence` / `getStreak` (stored streaks) / `getVolume` (last week's hard sets per muscle group), `progression.getPrSummary` / `listPrHistory` (stored PR history) / `getE1rmTrend` (estimated 1-rep max per session, per lift) / `getHistory`, `bodyMetrics.list` / `record` / `update` / `delete`
|
|
237
278
|
- Coach feedback on logged sessions: `sessionLogs.getFeedback` / `markFeedbackSeen` / `unseenFeedbackCount`
|
|
238
279
|
- 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
280
|
- 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)
|