@everfur/sdk 0.5.3 → 0.5.5

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +8 -8
  3. package/dist/{Chat-a6If51cF.d.cts → Chat-BBnXSZQG.d.cts} +20 -3
  4. package/dist/{Chat-CNRZEu92.d.ts → Chat-BzvoUe9L.d.ts} +20 -3
  5. package/dist/{DepthViews-zhn3Rk5Z.d.ts → DepthViews-BYEgUxDQ.d.ts} +1 -1
  6. package/dist/{DepthViews-Bfv2DTUl.d.ts → DepthViews-CsVphyQL.d.ts} +1 -1
  7. package/dist/{DepthViews-Cl4AoL3X.d.cts → DepthViews-D-pleytA.d.cts} +1 -1
  8. package/dist/{DepthViews-kURiusNd.d.cts → DepthViews-DKbfq4bX.d.cts} +1 -1
  9. package/dist/animations/index.d.cts +3 -3
  10. package/dist/animations/index.d.ts +3 -3
  11. package/dist/{attachments-CHgc11hT.d.cts → attachments-BcD6K0IF.d.cts} +5 -5
  12. package/dist/{attachments-TXOhN36S.d.ts → attachments-CFK3WPF4.d.ts} +5 -5
  13. package/dist/{attachments-affGN_Uv.d.cts → attachments-CieUG7nB.d.cts} +2 -2
  14. package/dist/{attachments-5_p7kE38.d.ts → attachments-DorefS77.d.ts} +2 -2
  15. package/dist/{bookingCopy-BQ7Pg7qg.d.ts → bookingCopy-BUTs_nKf.d.ts} +2 -2
  16. package/dist/{bookingCopy-BpZvZO1S.d.cts → bookingCopy-Cl3RZ_k2.d.cts} +2 -2
  17. package/dist/{casesRepository-YCI0OVwl.d.ts → casesRepository-C29WolRA.d.ts} +1 -1
  18. package/dist/{casesRepository-z2JAFlKA.d.cts → casesRepository-Ct5MO1mM.d.cts} +1 -1
  19. package/dist/chat/index.cjs +1 -1
  20. package/dist/chat/index.d.cts +9 -9
  21. package/dist/chat/index.d.ts +9 -9
  22. package/dist/chat/index.js +1 -1
  23. package/dist/client/index.cjs +2 -2
  24. package/dist/client/index.d.cts +6 -6
  25. package/dist/client/index.d.ts +6 -6
  26. package/dist/client/index.js +2 -2
  27. package/dist/{config-Bkbqkjb0.d.cts → config-BBeF3kv5.d.cts} +1 -1
  28. package/dist/{config-BXVgTLCh.d.ts → config-Dl8Iqs1i.d.ts} +1 -1
  29. package/dist/consent/index.d.cts +5 -5
  30. package/dist/consent/index.d.ts +5 -5
  31. package/dist/{context-ChPrlKhy.d.cts → context-DGZrfVkE.d.cts} +5 -5
  32. package/dist/{context-BVpOBDMe.d.ts → context-DXj75_Lo.d.ts} +5 -5
  33. package/dist/{copy-Be2H2i-D.d.cts → copy-CQHEQwW7.d.cts} +2 -2
  34. package/dist/{copy-SxmZ-Eps.d.ts → copy-CSbMmuCV.d.ts} +2 -2
  35. package/dist/core/index.cjs +7 -7
  36. package/dist/core/index.d.cts +15 -13
  37. package/dist/core/index.d.ts +15 -13
  38. package/dist/core/index.js +7 -7
  39. package/dist/{depth-Dne8Qoss.d.cts → depth-Bb2yphTT.d.cts} +2 -2
  40. package/dist/{depth-By8dFpLA.d.ts → depth-DRzDoKeE.d.ts} +2 -2
  41. package/dist/{entitlementRepository-Big-K_In.d.ts → entitlementRepository-S4bU03Ld.d.ts} +9 -2
  42. package/dist/{entitlementRepository-BmghvtGT.d.cts → entitlementRepository-lwXYn-lq.d.cts} +9 -2
  43. package/dist/{identity-AuoeDRTh.d.ts → identity-C0r18khM.d.ts} +1 -1
  44. package/dist/{identity-Z0yj-fC_.d.cts → identity-d2lu16uc.d.cts} +1 -1
  45. package/dist/index.cjs +6 -6
  46. package/dist/index.d.cts +12 -12
  47. package/dist/index.d.ts +12 -12
  48. package/dist/index.js +6 -6
  49. package/dist/notifications/index.d.cts +2 -2
  50. package/dist/notifications/index.d.ts +2 -2
  51. package/dist/{pets-DfXGYmMk.d.cts → pets-BZ0384Do.d.cts} +1 -1
  52. package/dist/{pets-FD-5nMXr.d.ts → pets-CJdq4rRX.d.ts} +1 -1
  53. package/dist/{petsRepository-CsvreWlY.d.cts → petsRepository-BHooDwb1.d.cts} +1 -1
  54. package/dist/{petsRepository-BaTOwJ5L.d.ts → petsRepository-DE8scL1O.d.ts} +1 -1
  55. package/dist/{ports-Dof7AQjk.d.cts → ports-CcUYur16.d.cts} +4 -4
  56. package/dist/{ports-DNIvfzBP.d.ts → ports-DWL3Nl3j.d.ts} +4 -4
  57. package/dist/records/depth/index.d.cts +7 -7
  58. package/dist/records/depth/index.d.ts +7 -7
  59. package/dist/records/index.d.cts +9 -9
  60. package/dist/records/index.d.ts +9 -9
  61. package/dist/{requestFunnel-CJC_q4BZ.d.ts → requestFunnel-C5LPty-S.d.ts} +2 -2
  62. package/dist/{requestFunnel-DrigK6q_.d.cts → requestFunnel-TtUvlQS1.d.cts} +2 -2
  63. package/dist/{runtimeTypes-Doj95Gll.d.cts → runtimeTypes-BbLoCs-9.d.cts} +12 -4
  64. package/dist/{runtimeTypes-BiqMY6pC.d.ts → runtimeTypes-CiJ6Y_wP.d.ts} +12 -4
  65. package/dist/server/events/index.cjs +1 -1
  66. package/dist/server/events/index.d.cts +2 -2
  67. package/dist/server/events/index.d.ts +2 -2
  68. package/dist/server/events/index.js +1 -1
  69. package/dist/server/index.cjs +2 -2
  70. package/dist/server/index.d.cts +3 -3
  71. package/dist/server/index.d.ts +3 -3
  72. package/dist/server/index.js +2 -2
  73. package/dist/televet/booking/index.cjs +1 -1
  74. package/dist/televet/booking/index.d.cts +6 -6
  75. package/dist/televet/booking/index.d.ts +6 -6
  76. package/dist/televet/booking/index.js +1 -1
  77. package/dist/testing/index.d.cts +1 -1
  78. package/dist/testing/index.d.ts +1 -1
  79. package/dist/testing/rn/index.d.cts +7 -7
  80. package/dist/testing/rn/index.d.ts +7 -7
  81. package/dist/testing/web/index.d.cts +7 -7
  82. package/dist/testing/web/index.d.ts +7 -7
  83. package/dist/{timelineRows-CHeqZRpM.d.cts → timelineRows-BZ4YqN5G.d.cts} +1 -1
  84. package/dist/{timelineRows-BHA0gIKH.d.ts → timelineRows-Bv7nr5Oz.d.ts} +1 -1
  85. package/dist/{useRecordsDepth-ChhS4c6r.d.cts → useRecordsDepth-BYVOh123.d.cts} +1 -1
  86. package/dist/{useRecordsDepth-CG63cXFZ.d.ts → useRecordsDepth-DuylsLXR.d.ts} +1 -1
  87. package/dist/{view-2pXv8ex7.d.cts → view-Cqtik97G.d.cts} +2 -2
  88. package/dist/{view-BKy44Q-3.d.ts → view-Uv0dWIQH.d.ts} +2 -2
  89. package/dist/web/consent/index.d.cts +5 -5
  90. package/dist/web/consent/index.d.ts +5 -5
  91. package/dist/web/index.cjs +10 -10
  92. package/dist/web/index.d.cts +14 -14
  93. package/dist/web/index.d.ts +14 -14
  94. package/dist/web/index.js +10 -10
  95. package/dist/web/notifications/index.d.cts +3 -3
  96. package/dist/web/notifications/index.d.ts +3 -3
  97. package/dist/web/records/depth/index.d.cts +7 -7
  98. package/dist/web/records/depth/index.d.ts +7 -7
  99. package/dist/web/records/index.cjs +3 -3
  100. package/dist/web/records/index.d.cts +9 -9
  101. package/dist/web/records/index.d.ts +9 -9
  102. package/dist/web/records/index.js +3 -3
  103. package/dist/web/televet/booking/index.cjs +2 -2
  104. package/dist/web/televet/booking/index.d.cts +9 -8
  105. package/dist/web/televet/booking/index.d.ts +9 -8
  106. package/dist/web/televet/booking/index.js +2 -2
  107. package/package.json +5 -4
package/CHANGELOG.md CHANGED
@@ -10,6 +10,115 @@ changes that has not been earned by any production integration yet.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.5.5] - 2026-09-26
14
+
15
+ ### Changed
16
+
17
+ - **A sandbox tenant's vet entry opens the visit simulator instead of "booking paused" (React Native and web).**
18
+ A self-serve sandbox tenant never has a televet allowance, so the in-app visit opened on the paused card with
19
+ nothing to do. When the entitlements report `livemode: false` and the televet entitlement read answers that
20
+ the allowance is off, the booking flow now shows the test-mode simulator in that place: create a simulated
21
+ visit for the pet (a household of several pets is asked which pet first), move it to completed, cancelled or
22
+ no-show, and raise the visit events that are not a status change, so `visit.*` webhooks arrive with
23
+ `livemode: false`. It calls only `POST /widget/v1/televet/sandbox/visits` and its `/advance` and `/simulate`
24
+ routes, never a hold or a booking. A live tenant, a verdict with no `livemode` (read as live), a sandbox tenant
25
+ that has an allowance (it keeps the real booking flow), a booking plane that is not served and a 402 at Confirm
26
+ are all unchanged. The simulator's wording is the partner demo apps' test-mode wording and awaits sign-off.
27
+ - **`EntitlementSnapshot` carries `livemode`.** The SDK now reads the tenant's `livemode` from
28
+ `GET /widget/v1/entitlements`. The snapshot carries `livemode: false` for a sandbox tenant and no key at all
29
+ otherwise, so a live tenant's snapshot is shaped as before; only the literal `false` counts as test.
30
+
31
+ ### Fixed
32
+
33
+ - **The chat composer clears the keyboard under a navigation header (React Native).** The chat's
34
+ `KeyboardAvoidingView` measured itself against its parent and passed no `keyboardVerticalOffset`, so under a
35
+ React Navigation or Expo Router stack header the lift fell short by the header's height and the composer stayed
36
+ behind the keyboard on iOS. The chat now measures the offset itself from its position in the window, with no
37
+ navigation dependency, and `EverfurChat` takes a new `keyboardVerticalOffset` prop that overrides it. Omitting
38
+ the prop is the recommendation: in a JavaScript-stack modal `useHeaderHeight()` is the header alone, short of
39
+ where the chat starts, and on iOS 26 the hook's first-render value is about 15 pt short, so never pass a copy
40
+ taken at mount. A reading taken while the screen is still animating in (a JS-stack modal's card
41
+ sliding up) is discarded and taken again every 100 ms, for up to about two seconds, until the chat is at rest,
42
+ so the offset has settled before the keyboard opens. A chat with nothing above it measures 0 and lays out
43
+ exactly as before; Android, where the window's resize does the lift, is unchanged.
44
+ - **No blank band above the chat under a header or in a modal (React Native, iOS).** The chat padded its top by
45
+ the device's whole safe-area inset even when a native-stack header or a modal's card already sat between it
46
+ and the Dynamic Island, which left about 62 pt of blank space. It now pads by only the part of the inset its
47
+ own top edge reaches into, from the window position it measures for the keyboard (also when you pass
48
+ `keyboardVerticalOffset`). A chat with nothing above it keeps the whole inset, exactly as before, and your
49
+ `style` still comes last and wins. Android is unchanged.
50
+ - **VoiceOver reads the chat composer's name once (React Native).** The composer's `accessibilityLabel` was its
51
+ placeholder, and iOS appends the placeholder to the label of an empty field, so it was read twice ("Ask me
52
+ anything! Ask me anything!"). The composer now carries no label of its own and the placeholder names it (the
53
+ hint on Android). No copy changed.
54
+ - **The chat thread scrolls inside a partner's pager or ScrollView on Android (React Native).** The thread, the
55
+ empty thread's starter prompts and the horizontal sources deck set no `nestedScrollEnabled`, so inside a
56
+ horizontal pager or a parent ScrollView the enclosing scroller took the drag and the thread did not move. All
57
+ three now opt in. iOS nests scrollers by default and is unchanged.
58
+ - **An empty urgency reason no longer draws an empty line in the banner.** The widget `/classify` now answers an
59
+ informational turn with `{"urgency": null, "reason": "", "flagged": false}` (EFBackend #1605). The null tier
60
+ already rendered the calm "Good to know" state with the SDK's own line, and tests now pin it. A tier that
61
+ arrived with an empty or blank reason drew an empty text line under its label on React Native (and a blank one
62
+ on the web). `latestUrgencySource` now reads a blank reason as no reason. No copy changed.
63
+ - **The README's documentation links open for a partner.** The README on npm linked eight files under
64
+ `docs/partner-integration/` and `examples/`, and npmjs.com resolves a README's relative links against the
65
+ package's `repository`, which is private: every one of them was a 404. Each now links to its section of the
66
+ public docs page on the SDK's CDN (`https://sdk.everfur.com/v1/docs/index.html#...`). `homepage` is that page,
67
+ and `bugs` is `support@everfur.com`, the contact the quickstart gives, instead of the private repository's
68
+ issue tracker. `repository` is unchanged: npm's trusted publishing requires it to match the repository that
69
+ publishes.
70
+ - **The "SDK disabled" console line names the web's own trap on the web.** A config the SDK cannot resolve
71
+ disables it and logs one line, ending with a hint about where a value set in the environment goes missing. The
72
+ hint named `babel-preset-expo` on every platform, so a Next.js app was pointed at an Expo bundler. On the web
73
+ it now says that a browser bundle does not expose `EVERFUR_API_BASE_URL` to the SDK and to pass `apiBaseUrl`
74
+ in config; React Native keeps the Expo hint. Developer log text only.
75
+
76
+ ### Added
77
+
78
+ - **Public partner docs on the SDK's CDN.** Each release publishes one page with the partner chapters of the doc
79
+ set to `https://sdk.everfur.com/<version>/docs/index.html` and the channel, `/v1/docs/index.html`. It is
80
+ rendered at build time (`scripts/build-public-docs.mjs`) and leaves out the operator runbooks and the backend
81
+ internals (`scripts/public-docs-policy.mjs` lists each exclusion and its reason); nothing is removed from the
82
+ repository. The build fails on an exclusion rule that no longer matches its source, a link the page cannot
83
+ resolve, or an internal marker in the page. A rollback (`promote`) to a version published before the page
84
+ existed keeps the channel's page rather than deleting it.
85
+
86
+ ### Internal
87
+
88
+ - A test fails when the README npm ships has a relative link (npmjs.com points it at the private repository) or
89
+ a link into `github.com/Everfur-Health`, and checks every README docs link against the anchors of the page the
90
+ build publishes.
91
+
92
+ ## [0.5.4] - 2026-09-25
93
+
94
+ ### Fixed
95
+
96
+ - **The in-page visit dialog stays modal while a screen holds Escape (web).** During the join hold, a keyboard
97
+ member's third Escape in a row closed the visit's native `<dialog>` (the partner web run of 2026-09-25, on 0.5.2
98
+ and 0.5.3): a browser lets a page refuse a dialog's close request only while it has user activation to spend,
99
+ each refusal spends it and Escape earns none, and the SDK refused only at the dialog's `cancel`. The page behind
100
+ became interactive, the busy notice stayed painted with no modal around it, and Not now then navigated inside a
101
+ closed dialog. The visit dialog now takes Escape at its keydown, where preventing it stops the close request
102
+ whatever the activation, and asks the screens as before. A close request that still reaches the dialog (Escape
103
+ with focus on the page body) is asked of the screens too, and when one of them keeps the visit, the dialog is
104
+ shown modal again with focus back where it was. The other web sheets keep answering Escape at `cancel`.
105
+ - **Focus lands inside the visit dialog at once on the way back to the visits (web).** After Not now, Leave or a
106
+ booking's Close, focus sat on the page body for as long as the visits list took to load again (0.6 to 5 seconds
107
+ on staging), and moved to the list's heading only once the list arrived. The loading panel's heading now takes
108
+ focus at once, and the list's first heading takes it once the list has loaded; a control the member moved to
109
+ meanwhile keeps it. No new copy.
110
+ - **Escape on the booking's discard confirm is Keep going (web).** In the in-page visit, Escape on "Leave
111
+ without booking?" did nothing: the confirm stayed open (real Chromium, React 18.2 and 19.3). The browser fires
112
+ `cancel` at the confirm only, but React ran the visit dialog's `onCancel` as well, whose answer (the booking's
113
+ Close) opened the confirm again in the same update. A web dialog now answers only a `cancel` fired at itself, so
114
+ Escape on the confirm keeps the member in the booking with what they typed, and focus goes back to Close.
115
+
116
+ ### Internal
117
+
118
+ - A regression test pins the visit dialog's re-show under React StrictMode (web): a browser close of a dialog the
119
+ SDK still renders open re-shows it once, with focus back, and StrictMode's second effect run leaves no extra
120
+ listener on the dialog. No behaviour change.
121
+
13
122
  ## [0.5.3] - 2026-09-25
14
123
 
15
124
  ### Fixed
package/README.md CHANGED
@@ -55,11 +55,11 @@ shipping the minter to a device. Import it from your server only.
55
55
  ### On the web
56
56
 
57
57
  Two ways, one wire contract, documented in
58
- [docs/partner-integration/13-WEB-INTEGRATION.md](docs/partner-integration/13-WEB-INTEGRATION.md):
58
+ [docs/partner-integration/13-WEB-INTEGRATION.md](https://sdk.everfur.com/v1/docs/index.html#sec-web):
59
59
 
60
60
  - **In-page**, for a page whose React tree you own: `import { EverfurProvider, EverfurChat } from
61
61
  '@everfur/sdk/web'` (peers: `react`, `react-dom`). The copy-paste shape is
62
- [`examples/minimal-web.tsx`](examples/minimal-web.tsx). Take the `/web` entry, not the package root: the
62
+ [`examples/minimal-web.tsx`](https://sdk.everfur.com/v1/docs/index.html#web--1-in-page-everfur-sdk-web). Take the `/web` entry, not the package root: the
63
63
  root is the React Native entry and imports `react-native` and `react-native-safe-area-context`, so in a
64
64
  browser project it fails to resolve them rather than rendering. The `/web` entries need `react`,
65
65
  `react/jsx-runtime` and `zod` and nothing native, which `npm run verify:pack` proves on every CI run by
@@ -67,7 +67,7 @@ Two ways, one wire contract, documented in
67
67
  - **Frame mode**, for a page you do not fully control (a storefront theme, a CMS page): one `<script
68
68
  src="https://sdk.everfur.com/v1/everfur.js">` puts the surface in an iframe on `sdk.everfur.com`, and
69
69
  the frame refuses to initialise on any page origin your tenant has not registered. The copy-paste shape
70
- is [`examples/frame-embed.html`](examples/frame-embed.html).
70
+ is [`examples/frame-embed.html`](https://sdk.everfur.com/v1/docs/index.html#web--the-snippet).
71
71
 
72
72
  Chat and records are the web surfaces (records on its own subpath and its own frame document, available
73
73
  when Everfur enables records for your account); photo and video remain React Native for now. Vet visits (not yet enabled)
@@ -78,7 +78,7 @@ dialog (web) that `VetVisitButton` and `EverfurChat` mount themselves; the hoste
78
78
  and the call (`@everfur/sdk/televet/call`, `@everfur/sdk/web/televet/call`) are their own entries, loaded on
79
79
  demand, so a page where nobody opens a visit downloads neither and a page where nobody joins takes no WebRTC
80
80
  dependency.
81
- [docs/partner-integration/16-TELEVET-VISITS.md](docs/partner-integration/16-TELEVET-VISITS.md).
81
+ [docs/partner-integration/16-TELEVET-VISITS.md](https://sdk.everfur.com/v1/docs/index.html#sec-televet).
82
82
 
83
83
  ## Quickstart
84
84
 
@@ -179,14 +179,14 @@ off-state, never a blank region.
179
179
  | --- | --- |
180
180
  | `@everfur/sdk` | `EverfurProvider`, `CapabilityGate`, `EverfurChat`, `useEverfurChat`, `CapabilityName`, error/result types, branded id helpers (`petRef`, `userRef`, `conversationId`) |
181
181
  | `@everfur/sdk/records` | `EverfurRecords` (the Everfur app's records flow), the named screens (`EverfurRecordsUpload`, `EverfurRecordsRequests`, `EverfurRecordsRequest`, `EverfurRecordDetail`, ...), `EverfurClinicRequest`, `EverfurClinicRequestBatch`, `SignaturePad`, records hooks + types |
182
- | `@everfur/sdk/records/depth`, `@everfur/sdk/web/records/depth` | record depth (the timeline, the withheld record sections, per-document contributions) on their own entries, dark until Everfur enables it per deployment; see [chapter 9](docs/partner-integration/09-GUIDE-RECORDS-AND-CONSENT.md#record-depth-behind-a-separate-switch) |
182
+ | `@everfur/sdk/records/depth`, `@everfur/sdk/web/records/depth` | record depth (the timeline, the withheld record sections, per-document contributions) on their own entries, dark until Everfur enables it per deployment; see [chapter 9](https://sdk.everfur.com/v1/docs/index.html#records--record-depth-behind-a-separate-switch) |
183
183
  | `@everfur/sdk/video` | gait / live-scan surface |
184
184
  | `@everfur/sdk/photo` | photo checkup surface |
185
185
  | `@everfur/sdk/animations` | server-driven pet-state media surface |
186
186
  | `@everfur/sdk/web` | the browser entry: `EverfurProvider`, `CapabilityGate`, `EverfurChat`, `useEverfurChat`, the theme hook, rendered with `react-dom`; withheld from React Native bundles the way `./server` is |
187
- | `@everfur/sdk/web/records` | records for a browser host: `EverfurRecordsPage` (the Everfur web app's records page, complete on its own: request from a clinic, upload, request status, the full record with vaccinations, all inside your page), the named screens, `EverfurRecords` (the React Native-shaped flow), `useEverfurRecords`, `createWebRecordsUploadTransport`, `fileToHandle`; its own entry, withheld from React Native like `./web`; available when Everfur enables records for the account. Frame mode serves it with `surface: 'records'` (see [chapter 13](docs/partner-integration/13-WEB-INTEGRATION.md)) |
187
+ | `@everfur/sdk/web/records` | records for a browser host: `EverfurRecordsPage` (the Everfur web app's records page, complete on its own: request from a clinic, upload, request status, the full record with vaccinations, all inside your page), the named screens, `EverfurRecords` (the React Native-shaped flow), `useEverfurRecords`, `createWebRecordsUploadTransport`, `fileToHandle`; its own entry, withheld from React Native like `./web`; available when Everfur enables records for the account. Frame mode serves it with `surface: 'records'` (see [chapter 13](https://sdk.everfur.com/v1/docs/index.html#web--2-frame-mode-the-loader-and-the-frame)) |
188
188
  | `@everfur/sdk/server` | **server-only** `mintPartnerSession` (no React Native resolution; never bundled on-device) |
189
- | `@everfur/sdk/server/events` | **server-only, Node only** partner events (preview): `constructEvent` to verify webhooks, `sendPartnerEvent`; see [chapter 15](docs/partner-integration/15-EVENTS-AND-WEBHOOKS.md) |
189
+ | `@everfur/sdk/server/events` | **server-only, Node only** partner events (preview): `constructEvent` to verify webhooks, `sendPartnerEvent`; see [chapter 15](https://sdk.everfur.com/v1/docs/index.html#sec-events) |
190
190
  | `@everfur/sdk/testing`, `@everfur/sdk/testing/rn`, `@everfur/sdk/testing/web` | test seams (`MockTransport`, `useEverfurTestEntitlements`, ...) for your own tests |
191
191
 
192
192
  `@everfur/sdk/client` and `@everfur/sdk/core` expose the framework-free layers for advanced integrators;
@@ -195,7 +195,7 @@ most apps only need the root entry plus the capability subpaths.
195
195
  ## Reference app
196
196
 
197
197
  A full, runnable integration (a fictional "Pawtrail" app plus a minimal mint backend that holds the secret)
198
- lives at [`examples/minimal-integration.tsx`](examples/minimal-integration.tsx) in this repository; it is
198
+ lives at [`examples/minimal-integration.tsx`](https://sdk.everfur.com/v1/docs/index.html#sec-quickstart) in this repository; it is
199
199
  type-checked by `npm run docs:check`, so it cannot rot. It shows anonymous mode, the two-credential session
200
200
  mint, pet registration and sign-out. (A separate `everfur-integration-example-rn` app exists internally but
201
201
  is not published anywhere a partner can reach, so do not rely on references to it.) It gates every
@@ -2,9 +2,9 @@ import { ReactNode } from 'react';
2
2
  import { StyleProp, ViewStyle } from 'react-native';
3
3
  import { P as PetRef, C as ConversationId } from './ids-B2GAAifq.cjs';
4
4
  import { F as FileHandle } from './ports-BN6RHF9W.cjs';
5
- import { a as ChatCheckinTarget } from './ports-Dof7AQjk.cjs';
6
- import { W as WidgetPet, P as PetProfileInput } from './petsRepository-CsvreWlY.cjs';
7
- import { U as UrgencyDisplayLevel, L as LifecyclePort, E as EverfurChatDraftStore } from './attachments-CHgc11hT.cjs';
5
+ import { a as ChatCheckinTarget } from './ports-CcUYur16.cjs';
6
+ import { W as WidgetPet, P as PetProfileInput } from './petsRepository-BHooDwb1.cjs';
7
+ import { U as UrgencyDisplayLevel, L as LifecyclePort, E as EverfurChatDraftStore } from './attachments-BcD6K0IF.cjs';
8
8
 
9
9
  /** Where a picked image comes from: the host's photo library, or its camera. */
10
10
  type AttachmentSource = 'photo' | 'camera';
@@ -132,6 +132,23 @@ interface EverfurChatProps {
132
132
  * `(url) => Linking.openURL(url)` for the Everfur app's system browser. A throw or a rejection is contained.
133
133
  */
134
134
  readonly onOpenUrl?: (url: string, kind: 'citation') => void | Promise<void>;
135
+ /**
136
+ * How far the top of the view that holds this chat sits below the top of the screen, in points: React
137
+ * Native's `KeyboardAvoidingView` prop of the same name, which the chat's own avoiding view receives, so the
138
+ * composer clears the keyboard. Absent (the default, and the recommendation), the chat measures it on iOS from
139
+ * its own position in the window, with no navigation dependency: under a React Navigation or Expo Router
140
+ * native-stack header, and in a JavaScript-stack modal, where it measures again until the card has finished
141
+ * sliding in. Nothing above the chat measures 0, the offset React Native uses when none is given. Android is
142
+ * unaffected: there the window's own resize lifts the composer.
143
+ *
144
+ * A value passed here is used as given, in place of the measurement. The obvious value is wrong in two cases,
145
+ * measured on an iPhone 17 simulator (iOS 26.5):
146
+ * - A JavaScript-stack modal: `useHeaderHeight()` returns the header alone (56), while the chat starts 128 pt
147
+ * down (the 62 pt status-bar margin, the card's 10 pt offset and the header). Omit the prop there.
148
+ * - iOS 26: `useHeaderHeight()` reads 100.67 on its first render and 116 after, so a value captured once at
149
+ * mount is about 15 pt short. Pass the hook's value from every render, never a copy taken at mount.
150
+ */
151
+ readonly keyboardVerticalOffset?: number;
135
152
  }
136
153
  /**
137
154
  * Entitlement-gated, four-state chat surface. Entitlement-OFF renders the CapabilityGate OffState;
@@ -2,9 +2,9 @@ import { ReactNode } from 'react';
2
2
  import { StyleProp, ViewStyle } from 'react-native';
3
3
  import { P as PetRef, C as ConversationId } from './ids-B2GAAifq.js';
4
4
  import { F as FileHandle } from './ports-BN6RHF9W.js';
5
- import { a as ChatCheckinTarget } from './ports-DNIvfzBP.js';
6
- import { W as WidgetPet, P as PetProfileInput } from './petsRepository-BaTOwJ5L.js';
7
- import { U as UrgencyDisplayLevel, L as LifecyclePort, E as EverfurChatDraftStore } from './attachments-TXOhN36S.js';
5
+ import { a as ChatCheckinTarget } from './ports-DWL3Nl3j.js';
6
+ import { W as WidgetPet, P as PetProfileInput } from './petsRepository-DE8scL1O.js';
7
+ import { U as UrgencyDisplayLevel, L as LifecyclePort, E as EverfurChatDraftStore } from './attachments-CFK3WPF4.js';
8
8
 
9
9
  /** Where a picked image comes from: the host's photo library, or its camera. */
10
10
  type AttachmentSource = 'photo' | 'camera';
@@ -132,6 +132,23 @@ interface EverfurChatProps {
132
132
  * `(url) => Linking.openURL(url)` for the Everfur app's system browser. A throw or a rejection is contained.
133
133
  */
134
134
  readonly onOpenUrl?: (url: string, kind: 'citation') => void | Promise<void>;
135
+ /**
136
+ * How far the top of the view that holds this chat sits below the top of the screen, in points: React
137
+ * Native's `KeyboardAvoidingView` prop of the same name, which the chat's own avoiding view receives, so the
138
+ * composer clears the keyboard. Absent (the default, and the recommendation), the chat measures it on iOS from
139
+ * its own position in the window, with no navigation dependency: under a React Navigation or Expo Router
140
+ * native-stack header, and in a JavaScript-stack modal, where it measures again until the card has finished
141
+ * sliding in. Nothing above the chat measures 0, the offset React Native uses when none is given. Android is
142
+ * unaffected: there the window's own resize lifts the composer.
143
+ *
144
+ * A value passed here is used as given, in place of the measurement. The obvious value is wrong in two cases,
145
+ * measured on an iPhone 17 simulator (iOS 26.5):
146
+ * - A JavaScript-stack modal: `useHeaderHeight()` returns the header alone (56), while the chat starts 128 pt
147
+ * down (the 62 pt status-bar margin, the card's 10 pt offset and the header). Omit the prop there.
148
+ * - iOS 26: `useHeaderHeight()` reads 100.67 on its first render and 116 after, so a value captured once at
149
+ * mount is about 15 pt short. Pass the hook's value from every render, never a copy taken at mount.
150
+ */
151
+ readonly keyboardVerticalOffset?: number;
135
152
  }
136
153
  /**
137
154
  * Entitlement-gated, four-state chat surface. Entitlement-OFF renders the CapabilityGate OffState;
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { StyleProp, ViewStyle } from 'react-native';
3
- import { f as RecordsScreenBaseProps } from './useRecordsDepth-CG63cXFZ.js';
3
+ import { f as RecordsScreenBaseProps } from './useRecordsDepth-DuylsLXR.js';
4
4
 
5
5
  interface EverfurRecordsDepthProps extends Pick<RecordsScreenBaseProps, 'petRef' | 'petName' | 'onNavigate' | 'onBack'> {
6
6
  readonly style?: StyleProp<ViewStyle>;
@@ -1,5 +1,5 @@
1
1
  import { CSSProperties, ReactNode } from 'react';
2
- import { f as RecordsScreenBaseProps } from './useRecordsDepth-CG63cXFZ.js';
2
+ import { f as RecordsScreenBaseProps } from './useRecordsDepth-DuylsLXR.js';
3
3
 
4
4
  interface EverfurRecordsDepthProps extends Pick<RecordsScreenBaseProps, 'petRef' | 'petName' | 'onNavigate' | 'onBack'> {
5
5
  readonly style?: CSSProperties;
@@ -1,5 +1,5 @@
1
1
  import { CSSProperties, ReactNode } from 'react';
2
- import { f as RecordsScreenBaseProps } from './useRecordsDepth-ChhS4c6r.cjs';
2
+ import { f as RecordsScreenBaseProps } from './useRecordsDepth-BYVOh123.cjs';
3
3
 
4
4
  interface EverfurRecordsDepthProps extends Pick<RecordsScreenBaseProps, 'petRef' | 'petName' | 'onNavigate' | 'onBack'> {
5
5
  readonly style?: CSSProperties;
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { StyleProp, ViewStyle } from 'react-native';
3
- import { f as RecordsScreenBaseProps } from './useRecordsDepth-ChhS4c6r.cjs';
3
+ import { f as RecordsScreenBaseProps } from './useRecordsDepth-BYVOh123.cjs';
4
4
 
5
5
  interface EverfurRecordsDepthProps extends Pick<RecordsScreenBaseProps, 'petRef' | 'petName' | 'onNavigate' | 'onBack'> {
6
6
  readonly style?: StyleProp<ViewStyle>;
@@ -1,8 +1,8 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { StyleProp, ViewStyle } from 'react-native';
3
3
  import { E as EverfurResult } from '../EverfurResult-ZtH-YV1h.cjs';
4
- import { A as AuthContext } from '../identity-Z0yj-fC_.cjs';
5
- export { I as IdentityProvider, n as newSessionId } from '../identity-Z0yj-fC_.cjs';
4
+ import { A as AuthContext } from '../identity-d2lu16uc.cjs';
5
+ export { I as IdentityProvider, n as newSessionId } from '../identity-d2lu16uc.cjs';
6
6
  import { P as PetRef } from '../ids-B2GAAifq.cjs';
7
7
  export { B as Brand, U as UserRef, _ as __brand } from '../ids-B2GAAifq.cjs';
8
8
  import { z } from 'zod';
@@ -13,7 +13,7 @@ import { E as EverfurError } from '../EverfurError-DmELDpXp.cjs';
13
13
  export { a as EverfurErrorCategory, b as EverfurErrorCode } from '../EverfurError-DmELDpXp.cjs';
14
14
  import { D as DisabledLevel, a as CapabilityDecision } from '../resolve-D4Ywz5OS.cjs';
15
15
  export { b as CapabilityDisabled, C as CapabilityName } from '../resolve-D4Ywz5OS.cjs';
16
- export { A as AcceptedFrame, a as AnalysisCompleteFrame, b as AnalysisStartedFrame, D as DeltaFrame, c as DoneFrame, E as ErrorFrame, d as EverfurRequest, e as EverfurRequestBase, F as Frame, f as FrameBase, g as FrameKind, H as HeartbeatFrame, h as HttpMethod, M as MultipartBody, T as TransportPort, U as UnknownFrame, i as UrgencyLevel } from '../config-Bkbqkjb0.cjs';
16
+ export { A as AcceptedFrame, a as AnalysisCompleteFrame, b as AnalysisStartedFrame, D as DeltaFrame, c as DoneFrame, E as ErrorFrame, d as EverfurRequest, e as EverfurRequestBase, F as Frame, f as FrameBase, g as FrameKind, H as HeartbeatFrame, h as HttpMethod, M as MultipartBody, T as TransportPort, U as UnknownFrame, i as UrgencyLevel } from '../config-BBeF3kv5.cjs';
17
17
 
18
18
  /**
19
19
  * The full /state payload. `degraded` + `cache_epoch` + `cache_key` are additive fields an older backend
@@ -1,8 +1,8 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { StyleProp, ViewStyle } from 'react-native';
3
3
  import { E as EverfurResult } from '../EverfurResult-SGvHKsCB.js';
4
- import { A as AuthContext } from '../identity-AuoeDRTh.js';
5
- export { I as IdentityProvider, n as newSessionId } from '../identity-AuoeDRTh.js';
4
+ import { A as AuthContext } from '../identity-C0r18khM.js';
5
+ export { I as IdentityProvider, n as newSessionId } from '../identity-C0r18khM.js';
6
6
  import { P as PetRef } from '../ids-B2GAAifq.js';
7
7
  export { B as Brand, U as UserRef, _ as __brand } from '../ids-B2GAAifq.js';
8
8
  import { z } from 'zod';
@@ -13,7 +13,7 @@ import { E as EverfurError } from '../EverfurError-DmELDpXp.js';
13
13
  export { a as EverfurErrorCategory, b as EverfurErrorCode } from '../EverfurError-DmELDpXp.js';
14
14
  import { D as DisabledLevel, a as CapabilityDecision } from '../resolve-D4Ywz5OS.js';
15
15
  export { b as CapabilityDisabled, C as CapabilityName } from '../resolve-D4Ywz5OS.js';
16
- export { A as AcceptedFrame, a as AnalysisCompleteFrame, b as AnalysisStartedFrame, D as DeltaFrame, c as DoneFrame, E as ErrorFrame, d as EverfurRequest, e as EverfurRequestBase, F as Frame, f as FrameBase, g as FrameKind, H as HeartbeatFrame, h as HttpMethod, M as MultipartBody, T as TransportPort, U as UnknownFrame, i as UrgencyLevel } from '../config-BXVgTLCh.js';
16
+ export { A as AcceptedFrame, a as AnalysisCompleteFrame, b as AnalysisStartedFrame, D as DeltaFrame, c as DoneFrame, E as ErrorFrame, d as EverfurRequest, e as EverfurRequestBase, F as Frame, f as FrameBase, g as FrameKind, H as HeartbeatFrame, h as HttpMethod, M as MultipartBody, T as TransportPort, U as UnknownFrame, i as UrgencyLevel } from '../config-Dl8Iqs1i.js';
17
17
 
18
18
  /**
19
19
  * The full /state payload. `degraded` + `cache_epoch` + `cache_key` are additive fields an older backend
@@ -1,12 +1,12 @@
1
1
  import { E as EverfurError } from './EverfurError-DmELDpXp.cjs';
2
2
  import { E as EverfurResult } from './EverfurResult-ZtH-YV1h.cjs';
3
3
  import { C as ConversationId, P as PetRef, b as ResponseId } from './ids-B2GAAifq.cjs';
4
- import { f as ChatFeedbackType, j as ChatStatus, i as ChatMessage, c as ChatConversationListOptions, d as ChatConversationPage, g as ChatHistoryOptions, h as ChatMessagePage, a as ChatCheckinTarget, e as ChatFeedbackReceipt } from './ports-Dof7AQjk.cjs';
5
- import { C as CheckinAnswer, a as CheckinOutcome } from './casesRepository-z2JAFlKA.cjs';
6
- import { i as UrgencyLevel } from './config-Bkbqkjb0.cjs';
7
- import { W as WidgetPet } from './petsRepository-CsvreWlY.cjs';
4
+ import { f as ChatFeedbackType, j as ChatStatus, i as ChatMessage, c as ChatConversationListOptions, d as ChatConversationPage, g as ChatHistoryOptions, h as ChatMessagePage, a as ChatCheckinTarget, e as ChatFeedbackReceipt } from './ports-CcUYur16.cjs';
5
+ import { C as CheckinAnswer, a as CheckinOutcome } from './casesRepository-Ct5MO1mM.cjs';
6
+ import { i as UrgencyLevel } from './config-BBeF3kv5.cjs';
7
+ import { W as WidgetPet } from './petsRepository-BHooDwb1.cjs';
8
8
  import { U as UploadTransport, F as FileHandle } from './ports-BN6RHF9W.cjs';
9
- import { c as AttachmentQueue, d as AttachmentState, A as AttachmentError, b as AttachmentOutcome } from './attachments-affGN_Uv.cjs';
9
+ import { c as AttachmentQueue, d as AttachmentState, A as AttachmentError, b as AttachmentOutcome } from './attachments-CieUG7nB.cjs';
10
10
 
11
11
  /** The three thumbs. `'neutral'` is the app's rotated thumbs-down glyph. */
12
12
  type ChatSentiment = 'up' | 'neutral' | 'down';
@@ -1,12 +1,12 @@
1
1
  import { E as EverfurError } from './EverfurError-DmELDpXp.js';
2
2
  import { E as EverfurResult } from './EverfurResult-SGvHKsCB.js';
3
3
  import { C as ConversationId, P as PetRef, b as ResponseId } from './ids-B2GAAifq.js';
4
- import { f as ChatFeedbackType, j as ChatStatus, i as ChatMessage, c as ChatConversationListOptions, d as ChatConversationPage, g as ChatHistoryOptions, h as ChatMessagePage, a as ChatCheckinTarget, e as ChatFeedbackReceipt } from './ports-DNIvfzBP.js';
5
- import { C as CheckinAnswer, a as CheckinOutcome } from './casesRepository-YCI0OVwl.js';
6
- import { i as UrgencyLevel } from './config-BXVgTLCh.js';
7
- import { W as WidgetPet } from './petsRepository-BaTOwJ5L.js';
4
+ import { f as ChatFeedbackType, j as ChatStatus, i as ChatMessage, c as ChatConversationListOptions, d as ChatConversationPage, g as ChatHistoryOptions, h as ChatMessagePage, a as ChatCheckinTarget, e as ChatFeedbackReceipt } from './ports-DWL3Nl3j.js';
5
+ import { C as CheckinAnswer, a as CheckinOutcome } from './casesRepository-C29WolRA.js';
6
+ import { i as UrgencyLevel } from './config-Dl8Iqs1i.js';
7
+ import { W as WidgetPet } from './petsRepository-DE8scL1O.js';
8
8
  import { U as UploadTransport, F as FileHandle } from './ports-BN6RHF9W.js';
9
- import { c as AttachmentQueue, d as AttachmentState, A as AttachmentError, b as AttachmentOutcome } from './attachments-5_p7kE38.js';
9
+ import { c as AttachmentQueue, d as AttachmentState, A as AttachmentError, b as AttachmentOutcome } from './attachments-DorefS77.js';
10
10
 
11
11
  /** The three thumbs. `'neutral'` is the app's rotated thumbs-down glyph. */
12
12
  type ChatSentiment = 'up' | 'neutral' | 'down';
@@ -1,7 +1,7 @@
1
1
  import { E as EverfurError } from './EverfurError-DmELDpXp.cjs';
2
- import { R as RequestFunnel } from './requestFunnel-DrigK6q_.cjs';
2
+ import { R as RequestFunnel } from './requestFunnel-TtUvlQS1.cjs';
3
3
  import { F as FileHandle, U as UploadTransport, a as UploadPolicyWire } from './ports-BN6RHF9W.cjs';
4
- import { R as ReadableStore, T as Timers } from './ports-Dof7AQjk.cjs';
4
+ import { R as ReadableStore, T as Timers } from './ports-CcUYur16.cjs';
5
5
 
6
6
  /** The image MIME types the chat accepts (the server's `CHAT_ATTACHMENT` rule, plus the `image/jpg` alias). */
7
7
  declare const ATTACHMENT_MIME_TYPES: readonly string[];
@@ -1,7 +1,7 @@
1
1
  import { E as EverfurError } from './EverfurError-DmELDpXp.js';
2
- import { R as RequestFunnel } from './requestFunnel-CJC_q4BZ.js';
2
+ import { R as RequestFunnel } from './requestFunnel-C5LPty-S.js';
3
3
  import { F as FileHandle, U as UploadTransport, a as UploadPolicyWire } from './ports-BN6RHF9W.js';
4
- import { R as ReadableStore, T as Timers } from './ports-DNIvfzBP.js';
4
+ import { R as ReadableStore, T as Timers } from './ports-DWL3Nl3j.js';
5
5
 
6
6
  /** The image MIME types the chat accepts (the server's `CHAT_ATTACHMENT` rule, plus the `image/jpg` alias). */
7
7
  declare const ATTACHMENT_MIME_TYPES: readonly string[];
@@ -1,8 +1,8 @@
1
1
  import { a as TelevetStatePolicy, b as TelevetBookingEntitlement, c as TelevetCancellationPolicy, d as TelevetClinician, e as TelevetSlot, f as TelevetSlotHold, g as TelevetBookingBody, T as TelevetConsult, h as TelevetCancelReceipt, C as ConsultStatus } from './bookingTypes-DCs8mmBb.js';
2
2
  import { E as EverfurError } from './EverfurError-DmELDpXp.js';
3
3
  import { E as EverfurResult } from './EverfurResult-SGvHKsCB.js';
4
- import { R as RequestFunnel } from './requestFunnel-CJC_q4BZ.js';
5
- import { A as AuthContext } from './identity-AuoeDRTh.js';
4
+ import { R as RequestFunnel } from './requestFunnel-C5LPty-S.js';
5
+ import { A as AuthContext } from './identity-C0r18khM.js';
6
6
  import { E as ErrorPolicyPort } from './ErrorPolicyPort-D22LUVrK.js';
7
7
  import { T as TelemetryPort } from './TelemetryPort-BDNr00hu.js';
8
8
  import { b as VetCallCredential, c as VetCallJoinRefusal } from './callRoom-7wrE3RP2.js';
@@ -1,8 +1,8 @@
1
1
  import { a as TelevetStatePolicy, b as TelevetBookingEntitlement, c as TelevetCancellationPolicy, d as TelevetClinician, e as TelevetSlot, f as TelevetSlotHold, g as TelevetBookingBody, T as TelevetConsult, h as TelevetCancelReceipt, C as ConsultStatus } from './bookingTypes-DCs8mmBb.cjs';
2
2
  import { E as EverfurError } from './EverfurError-DmELDpXp.cjs';
3
3
  import { E as EverfurResult } from './EverfurResult-ZtH-YV1h.cjs';
4
- import { R as RequestFunnel } from './requestFunnel-DrigK6q_.cjs';
5
- import { A as AuthContext } from './identity-Z0yj-fC_.cjs';
4
+ import { R as RequestFunnel } from './requestFunnel-TtUvlQS1.cjs';
5
+ import { A as AuthContext } from './identity-d2lu16uc.cjs';
6
6
  import { E as ErrorPolicyPort } from './ErrorPolicyPort-D6CybiqJ.cjs';
7
7
  import { T as TelemetryPort } from './TelemetryPort-BDNr00hu.cjs';
8
8
  import { b as VetCallCredential, c as VetCallJoinRefusal } from './callRoom-B_EqaUzx.cjs';
@@ -1,5 +1,5 @@
1
1
  import { E as EverfurResult } from './EverfurResult-SGvHKsCB.js';
2
- import { A as AuthContext } from './identity-AuoeDRTh.js';
2
+ import { A as AuthContext } from './identity-C0r18khM.js';
3
3
  import { P as PetRef } from './ids-B2GAAifq.js';
4
4
 
5
5
  /** The owner's answer to a check-in, the wire values of the consumer `CheckinResponse` enum. */
@@ -1,5 +1,5 @@
1
1
  import { E as EverfurResult } from './EverfurResult-ZtH-YV1h.cjs';
2
- import { A as AuthContext } from './identity-Z0yj-fC_.cjs';
2
+ import { A as AuthContext } from './identity-d2lu16uc.cjs';
3
3
  import { P as PetRef } from './ids-B2GAAifq.cjs';
4
4
 
5
5
  /** The owner's answer to a check-in, the wire values of the consumer `CheckinResponse` enum. */