@everfur/sdk 0.5.1 → 0.5.3

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 (130) hide show
  1. package/CHANGELOG.md +351 -0
  2. package/dist/BookedScreen-BJPbXLCG.d.ts +54 -0
  3. package/dist/BookedScreen-Cnld-jB_.d.cts +54 -0
  4. package/dist/{Chat-Rzplrf5M.d.ts → Chat-CNRZEu92.d.ts} +3 -3
  5. package/dist/{Chat-ypo6miKm.d.cts → Chat-a6If51cF.d.cts} +3 -3
  6. package/dist/{DepthViews-CeTfPUBX.d.ts → DepthViews-Bfv2DTUl.d.ts} +1 -1
  7. package/dist/{DepthViews-Cui3oonj.d.cts → DepthViews-Cl4AoL3X.d.cts} +1 -1
  8. package/dist/{DepthViews-ClykXOJj.d.cts → DepthViews-kURiusNd.d.cts} +1 -1
  9. package/dist/{DepthViews-baYLMNQ-.d.ts → DepthViews-zhn3Rk5Z.d.ts} +1 -1
  10. package/dist/animations/index.d.cts +3 -3
  11. package/dist/animations/index.d.ts +3 -3
  12. package/dist/{attachments-D3HCBmOE.d.ts → attachments-5_p7kE38.d.ts} +2 -2
  13. package/dist/{attachments-CwsR-bPN.d.cts → attachments-CHgc11hT.d.cts} +5 -5
  14. package/dist/{attachments-CzZ7in24.d.ts → attachments-TXOhN36S.d.ts} +5 -5
  15. package/dist/{attachments-Nk73Lll8.d.cts → attachments-affGN_Uv.d.cts} +2 -2
  16. package/dist/{bookingCopy-SDkXaxDf.d.ts → bookingCopy-BQ7Pg7qg.d.ts} +38 -200
  17. package/dist/{bookingCopy-Dv8rKqyV.d.cts → bookingCopy-BpZvZO1S.d.cts} +38 -200
  18. package/dist/bookingTypes-DCs8mmBb.d.cts +188 -0
  19. package/dist/bookingTypes-DCs8mmBb.d.ts +188 -0
  20. package/dist/{callCopy-rDH9JTFo.d.cts → callCopy-CPe9mbwG.d.cts} +7 -2
  21. package/dist/{callCopy-rDH9JTFo.d.ts → callCopy-CPe9mbwG.d.ts} +7 -2
  22. package/dist/{casesRepository-BTAecTnF.d.ts → casesRepository-YCI0OVwl.d.ts} +1 -1
  23. package/dist/{casesRepository-aryld7fJ.d.cts → casesRepository-z2JAFlKA.d.cts} +1 -1
  24. package/dist/chat/index.cjs +1 -1
  25. package/dist/chat/index.d.cts +14 -10
  26. package/dist/chat/index.d.ts +14 -10
  27. package/dist/chat/index.js +1 -1
  28. package/dist/client/index.cjs +1 -1
  29. package/dist/client/index.d.cts +6 -6
  30. package/dist/client/index.d.ts +6 -6
  31. package/dist/client/index.js +1 -1
  32. package/dist/{config-X6BtwDwQ.d.ts → config-BXVgTLCh.d.ts} +1 -1
  33. package/dist/{config-6qS7Sohy.d.cts → config-Bkbqkjb0.d.cts} +1 -1
  34. package/dist/consent/index.d.cts +5 -5
  35. package/dist/consent/index.d.ts +5 -5
  36. package/dist/{context-7f3Nb_xE.d.ts → context-BVpOBDMe.d.ts} +5 -5
  37. package/dist/{context-CkqPM4Ml.d.cts → context-ChPrlKhy.d.cts} +5 -5
  38. package/dist/{copy-Bd__Pw5s.d.cts → copy-Be2H2i-D.d.cts} +2 -2
  39. package/dist/{copy-NgKgOOET.d.ts → copy-SxmZ-Eps.d.ts} +2 -2
  40. package/dist/core/index.cjs +5 -5
  41. package/dist/core/index.d.cts +12 -12
  42. package/dist/core/index.d.ts +12 -12
  43. package/dist/core/index.js +5 -5
  44. package/dist/{depth-CYPH92jE.d.ts → depth-By8dFpLA.d.ts} +2 -2
  45. package/dist/{depth-CyjaJJ6V.d.cts → depth-Dne8Qoss.d.cts} +2 -2
  46. package/dist/{entitlementRepository-DdeFuEKp.d.ts → entitlementRepository-Big-K_In.d.ts} +2 -2
  47. package/dist/{entitlementRepository-Dl1qRLcJ.d.cts → entitlementRepository-BmghvtGT.d.cts} +2 -2
  48. package/dist/{identity-Dy_L7Yq4.d.ts → identity-AuoeDRTh.d.ts} +1 -1
  49. package/dist/{identity-DjqhDm5Z.d.cts → identity-Z0yj-fC_.d.cts} +1 -1
  50. package/dist/index.cjs +6 -6
  51. package/dist/index.d.cts +13 -13
  52. package/dist/index.d.ts +13 -13
  53. package/dist/index.js +6 -6
  54. package/dist/notifications/index.d.cts +2 -2
  55. package/dist/notifications/index.d.ts +2 -2
  56. package/dist/{pets-BSG2qbr_.d.cts → pets-DfXGYmMk.d.cts} +1 -1
  57. package/dist/{pets-D7cZutrX.d.ts → pets-FD-5nMXr.d.ts} +1 -1
  58. package/dist/{petsRepository-DhsX3GLw.d.ts → petsRepository-BaTOwJ5L.d.ts} +1 -1
  59. package/dist/{petsRepository-D1bnxDZg.d.cts → petsRepository-CsvreWlY.d.cts} +1 -1
  60. package/dist/{ports-C0m1G1fS.d.ts → ports-DNIvfzBP.d.ts} +16 -5
  61. package/dist/{ports-DNXyyzZF.d.cts → ports-Dof7AQjk.d.cts} +16 -5
  62. package/dist/records/depth/index.d.cts +7 -7
  63. package/dist/records/depth/index.d.ts +7 -7
  64. package/dist/records/index.cjs +3 -3
  65. package/dist/records/index.d.cts +10 -10
  66. package/dist/records/index.d.ts +10 -10
  67. package/dist/records/index.js +3 -3
  68. package/dist/{requestFunnel-FvE5yLDD.d.ts → requestFunnel-CJC_q4BZ.d.ts} +2 -2
  69. package/dist/{requestFunnel-Bqc1inM9.d.cts → requestFunnel-DrigK6q_.d.cts} +2 -2
  70. package/dist/{runtimeTypes-C0yPBz89.d.ts → runtimeTypes-BiqMY6pC.d.ts} +4 -4
  71. package/dist/{runtimeTypes-RN-VXTFV.d.cts → runtimeTypes-Doj95Gll.d.cts} +4 -4
  72. package/dist/server/events/index.cjs +1 -1
  73. package/dist/server/events/index.d.cts +2 -2
  74. package/dist/server/events/index.d.ts +2 -2
  75. package/dist/server/events/index.js +1 -1
  76. package/dist/server/index.cjs +2 -2
  77. package/dist/server/index.d.cts +3 -3
  78. package/dist/server/index.d.ts +3 -3
  79. package/dist/server/index.js +2 -2
  80. package/dist/televet/booking/index.cjs +1 -1
  81. package/dist/televet/booking/index.d.cts +17 -9
  82. package/dist/televet/booking/index.d.ts +17 -9
  83. package/dist/televet/booking/index.js +1 -1
  84. package/dist/televet/call/index.cjs +1 -1
  85. package/dist/televet/call/index.d.cts +13 -1
  86. package/dist/televet/call/index.d.ts +13 -1
  87. package/dist/televet/call/index.js +1 -1
  88. package/dist/televet/index.cjs +1 -1
  89. package/dist/televet/index.js +1 -1
  90. package/dist/testing/index.d.cts +1 -1
  91. package/dist/testing/index.d.ts +1 -1
  92. package/dist/testing/rn/index.d.cts +7 -7
  93. package/dist/testing/rn/index.d.ts +7 -7
  94. package/dist/testing/web/index.d.cts +7 -7
  95. package/dist/testing/web/index.d.ts +7 -7
  96. package/dist/{timelineRows-D4U6A8zE.d.ts → timelineRows-BHA0gIKH.d.ts} +1 -1
  97. package/dist/{timelineRows-DAB5dpjF.d.cts → timelineRows-CHeqZRpM.d.cts} +1 -1
  98. package/dist/{uploadTransport-DKJHs3Yj.d.cts → uploadTransport-C1p8FuG8.d.cts} +5 -1
  99. package/dist/{uploadTransport-D0M0T4hN.d.ts → uploadTransport-bZIKBTyK.d.ts} +5 -1
  100. package/dist/{useRecordsDepth--ObtMyjH.d.ts → useRecordsDepth-CG63cXFZ.d.ts} +1 -1
  101. package/dist/{useRecordsDepth-B35y8bV-.d.cts → useRecordsDepth-ChhS4c6r.d.cts} +1 -1
  102. package/dist/{view-B-Toym_j.d.cts → view-2pXv8ex7.d.cts} +7 -4
  103. package/dist/{view-D9JqNQai.d.ts → view-BKy44Q-3.d.ts} +7 -4
  104. package/dist/web/consent/index.d.cts +5 -5
  105. package/dist/web/consent/index.d.ts +5 -5
  106. package/dist/web/index.cjs +10 -10
  107. package/dist/web/index.d.cts +14 -14
  108. package/dist/web/index.d.ts +14 -14
  109. package/dist/web/index.js +10 -10
  110. package/dist/web/notifications/index.d.cts +3 -3
  111. package/dist/web/notifications/index.d.ts +3 -3
  112. package/dist/web/records/depth/index.d.cts +7 -7
  113. package/dist/web/records/depth/index.d.ts +7 -7
  114. package/dist/web/records/index.cjs +3 -3
  115. package/dist/web/records/index.d.cts +9 -9
  116. package/dist/web/records/index.d.ts +9 -9
  117. package/dist/web/records/index.js +3 -3
  118. package/dist/web/televet/booking/index.cjs +2 -2
  119. package/dist/web/televet/booking/index.d.cts +35 -50
  120. package/dist/web/televet/booking/index.d.ts +35 -50
  121. package/dist/web/televet/booking/index.js +2 -2
  122. package/dist/web/televet/call/index.cjs +1 -1
  123. package/dist/web/televet/call/index.d.cts +12 -5
  124. package/dist/web/televet/call/index.d.ts +12 -5
  125. package/dist/web/televet/call/index.js +1 -1
  126. package/dist/web/televet/index.cjs +4 -1
  127. package/dist/web/televet/index.d.cts +31 -2
  128. package/dist/web/televet/index.d.ts +31 -2
  129. package/dist/web/televet/index.js +4 -1
  130. package/package.json +4 -3
package/CHANGELOG.md CHANGED
@@ -10,6 +10,357 @@ changes that has not been earned by any production integration yet.
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.5.3] - 2026-09-25
14
+
15
+ ### Fixed
16
+
17
+ - **The in-page join screen's hold ends at the join request's deadline, whatever `getToken` does (web).** The
18
+ in-page join screen holds Escape while the join request travels, as the consumer web app holds its decline
19
+ then, and nothing in the screen ended that hold: a stalled token refresh left the member in the dialog with a
20
+ disabled spinner and no way out but Back or a reload. That hold, with Not now disabled beside it, now lasts at
21
+ most 30 seconds (the request's own deadline, `FUNNEL_UNARY_TIMEOUT_MS`): the partner's `getToken`, which runs
22
+ before the request on first use and on the re-mint after a 401, and a host-injected `controller` have no
23
+ deadline of their own, and the dialog has no close control. Past it, Escape and Not now are Leave, and an
24
+ answer that arrives after the member left opens nothing. Not now is also enabled while the call surface
25
+ downloads after the request has answered, where Escape already left.
26
+
27
+ ## [0.5.2] - 2026-09-25
28
+
29
+ Fixes for the televet booking, web vet call and web dialog defects found on staging on 2026-09-23, for the
30
+ React Native vet call, and for the records upload and chat defects the partner end-to-end runs found on
31
+ 2026-09-24, with the missing React Native pull to refresh on a records request. Public signature changes are
32
+ additive only, each an optional prop, a new method, a new export or a promise from a method that returned
33
+ nothing: `selectedSlot` on `TelevetSlotPicker` (both platforms); `consultMinutes` on the React Native
34
+ `EverfurVetCall` and `EverfurVetCallLobby` (`@everfur/sdk/televet/call`, see the call lobby entry under Fixed);
35
+ `ChatController.newConversation()` (see Added); a promise from `refresh()` on `useEverfurRecords` (both
36
+ platforms, see Changed); and on web, `onAddToCalendar` on `EverfurTelevetHost` and `EverfurTelevetDialog`,
37
+ `returnedTo` on `TelevetVisitsScreen`, `lengthMinutes` on `TelevetBookedScreen`, a ready `.ics` calendar writer
38
+ exported from `@everfur/sdk/web/televet`, and a third result a web `TelevetCalendarWriter` may resolve,
39
+ `'handed-off'` (a writer that resolves a boolean is unchanged).
40
+
41
+ ### Added
42
+
43
+ - **`EverfurTelevetHost` takes `onAddToCalendar` (web).** The booked confirmation has had a calendar seam since
44
+ the web booking shipped, but the default in-page visit could not reach it: the host took no props and its
45
+ dialog rendered the flow without a writer, so a partner on the default path could not offer Add to Calendar
46
+ even with a calendar of its own. Pass a `TelevetCalendarWriter` to `EverfurTelevetHost` and the confirmation
47
+ offers the control and calls the writer with the visit's entry; without one it has no calendar control, as
48
+ before. The hosts `VetVisitButton` and `EverfurChat` mount take no props, and the first host mounted renders,
49
+ so mount yours above them. `EverfurTelevetHostProps`, `TelevetCalendarEvent` and `TelevetCalendarWriter` are
50
+ exported from `@everfur/sdk/web/televet` (types only; the entry's code does not grow by the booking screens).
51
+ `EverfurTelevetDialog` takes the same prop.
52
+ - **`downloadTelevetCalendarFile`, a ready calendar writer (web).** The consumer web app's Add to Calendar hands
53
+ the member a one-shot `.ics` file, because a browser has no calendar a page may write to. The SDK now ships
54
+ that file's builder, ported from the consumer's: `<EverfurTelevetHost onAddToCalendar={downloadTelevetCalendarFile} />`
55
+ offers Add to Calendar with no calendar code of your own. The file is RFC 5545 in UTC with CRLF line ends and
56
+ 75-octet folding, its `UID` is the entry's `key`, and it carries no alarm (a file cannot be withdrawn when a
57
+ visit moves). An entry that would be wrong (a blank title, an unreadable time, an end not after the start) is
58
+ not built, and the confirmation shows its existing did-not-save line. A started download is not an entry in a
59
+ calendar (the member still imports the file, and may cancel the save or have it blocked), so the writer resolves
60
+ `'handed-off'`, a result the web `TelevetCalendarWriter` now allows beside `true` and `false`, and on it the
61
+ confirmation keeps Add to Calendar enabled to press again, as the consumer web app's link does, rather than
62
+ saying the visit was added. Only a writer that resolves `true` gets "Added to your calendar".
63
+ `televetCalendarFile(event)` returns the same file as `{ href, filename }` for a page that renders its own link.
64
+ Both are exported from `@everfur/sdk/web/televet` with `TelevetCalendarFile`; no new copy.
65
+ - **`TelevetSlotPicker` takes an optional `selectedSlot`.** The strip opens on that slot's day instead of
66
+ the first offered day when the day is offered. Omitted, the picker behaves as before. Both platforms.
67
+ - **`ChatController.newConversation()`.** Leave the active thread for a fresh, empty one. It is what
68
+ `newConversation()` on `useEverfurChat` now calls. `adoptConversation(null)` is unchanged and still clears the
69
+ id alone. Both platforms.
70
+
71
+ ### Changed
72
+
73
+ - **`refresh()` on `useEverfurRecords` returns a promise.** It settles once the reads it started have landed,
74
+ which is what the React Native request screen's pull-to-refresh spinner waits for. It was typed `void`; a
75
+ caller that ignores the result is unaffected. Both platforms.
76
+
77
+ ### Fixed
78
+
79
+ - **The chat follows the member to the answer.** On the web, after a send the new question and its reply
80
+ rendered below the visible transcript. The transcript followed new content only while it believed the reader
81
+ was at the end, and three things broke that: it read its own scroll, a frame later, as the reader's, and on a
82
+ resumed thread the follow-up list had grown under the reply by then, so the reader looked 709 px short of the
83
+ end; nothing set it back on a send; and when it did follow, it went to the end of the follow-up list, which put
84
+ the question and its answer above the view. The transcript now settles on the latest turn, the top of the
85
+ newest reply with the question above it, as Everfur's own apps do (`resolveTurnScrollTarget`, ported). Every
86
+ turn the member starts (a typed send, a suggested prompt, Retry) takes them there even from further up, and the
87
+ surface no longer mistakes its own scroll for the reader's. A reader who scrolls up to re-read still keeps their
88
+ place when a message they did not ask for arrives. The React Native thread never scrolled itself at all, so a
89
+ resumed thread opened on its first message and every send landed below the fold; it now follows the same way,
90
+ measured with `onLayout` as the Everfur app measures its own thread. Both platforms.
91
+ - **New chat clears the thread.** `newConversation()` on `useEverfurChat` (behind the new-chat controls in the
92
+ header and the history panel, and `Start a new chat` under a turn that cannot be retried) only cleared the
93
+ conversation id. The old thread stayed on screen, and the next message opened a new conversation on the wire
94
+ but rendered under the old one. It now leaves the thread: the transcript clears with its history cursor, a
95
+ failed or stopped turn and the reply's own follow-ups; a reply still streaming into it is dropped; and the next
96
+ message is the first turn of the new conversation. The suggested prompts the chat opened with stay, as does the
97
+ unread dot for the member's other threads. Both platforms.
98
+ - **A chat photo uploads on an Expo app (React Native).** On a partner's Expo SDK 57 app every picked photo
99
+ failed: the initiate call answered 200 three times and no request reached S3, then the composer held "Some
100
+ attachments failed to upload. Remove or retry them before sending." Expo replaces the global `fetch` with
101
+ `expo/fetch`, whose multipart encoder throws on React Native's `{ uri, name, type }` file part before any
102
+ request exists, and the transport posted through the global `fetch`. `createRnChatAttachmentTransport` now
103
+ posts through React Native's own `XMLHttpRequest` (Expo leaves it in place), which streams the file from disk
104
+ as before: the policy fields in the server's order, the file last, no cookie, no header of its own, and the
105
+ same two bounded rejections. The `fetch` option is still honoured when given.
106
+ - **A failed attachment draws its retry control (React Native).** The alert said "Remove or retry", but the red
107
+ overlay that retries (`Retry uploading {filename}`) was never drawn, and neither were the uploading wash or the
108
+ upload flash. Their box came from `StyleSheet.absoluteFillObject`, which React Native 0.86 no longer has; a
109
+ spread of it is `{}`, so each overlay fell into the flow at zero size. The same spread placed the chat header's
110
+ title layer and the feedback sheet's scrim. They now use a literal box that holds on every React Native version.
111
+ - **The React Native vet call keeps its video and camera-off panels full-size.** `EverfurVetCall` took the box
112
+ for the clinician's video and its frame, the self-view video, the self-view's camera-off overlay and the
113
+ clinician's camera-off panel from the same `StyleSheet.absoluteFillObject` spread, so on React Native 0.86 each
114
+ fell into the flow at zero size during a call. They now use the same literal box. The scrims behind the chat
115
+ history drawer, the pet switcher and the records edit and share sheets used `StyleSheet.absoluteFill`, which
116
+ still works, and now use that box too, so no React Native surface takes an overlay box from `StyleSheet`.
117
+ - **Photo attach works under React StrictMode (web, and any React Native host that uses it).** In development
118
+ under `<StrictMode>` (on by default in Vite and Next), every valid photo was refused with "You can attach up to
119
+ 5 files per message." at 0 of 5, and nothing uploaded. StrictMode's remount ran the hook's cleanup, which
120
+ disposed the attachment queue the hook kept handing out. The remount now builds the scope a fresh queue.
121
+ Production builds were not affected.
122
+ - **Booking a slot again after cancelling it books a new visit.** A member booked a slot, cancelled the visit
123
+ in-app, then booked the same slot again. The hold and the booking both succeeded, but the booking answer was
124
+ the first visit, now cancelled, and the flow confirmed it ("You're booked with {vet}" on the web, "You are
125
+ booked." on React Native). No new visit existed. The booking's `Idempotency-Key` was derived from the slot
126
+ alone (`televet.book.<slotId>`), so the platform read the second booking as a retry of the first and
127
+ answered with the first consult as it now stood.
128
+ `televetBookingKey` now mints a new key on every call, and both platforms call it once per awarded hold, so a
129
+ new hold is a new booking even on the same slot. Pressing Confirm again after a failure still reuses that
130
+ hold's key, so a lost answer cannot become a second visit. If you drive the booking reducer yourself, call
131
+ `televetBookingKey` once when a hold is awarded and keep the result in the draft; calling it at submit time
132
+ would give each retry its own key. Both platforms.
133
+ - **A booking answered with a visit that is not live is never confirmed.** `TelevetBookingController.book`
134
+ used to settle any readable 2xx as a booking. It now settles a consult outside the platform's live statuses
135
+ (`scheduled`, `in_progress`, `awaiting_summary`) as a refusal: a cancelled, no-show, completed or abandoned
136
+ visit, or a status this build does not know. The booking flow shows it as the existing slot-taken refusal
137
+ ("That time was just taken. Pick another.") and returns to the time grid, where picking a time takes a new
138
+ hold, and so a new key. `onBooked` is not called. The flow checks the same thing before it draws the
139
+ confirmation, so a controller you inject through the `controller` prop is held to it too. Both platforms.
140
+ - **A booking whose key the platform refuses goes back to the time grid, not round Confirm.** From EFBackend
141
+ #1480 the platform answers a booking whose `Idempotency-Key` names a visit that has ended, or a live visit on
142
+ another slot or for another pet, with 409 `IDEMPOTENCY_KEY_REUSED_DIFFERENT_REQUEST`, and records nothing.
143
+ The booking flow read that as a retryable failure and left Confirm live on the review step, and Confirm
144
+ resent the same key, so every press got the same 409. `TelevetBookingController.book` now gives that refusal
145
+ the reason code `televet_booking_key_reused`, and the flow shows it as the existing slot-taken refusal ("That
146
+ time was just taken. Pick another.") on the time grid, where picking a time takes a new hold, and so a new
147
+ key. `onBooked` is not called. The error code stays `idempotencyConflict` and is still not retryable, and no
148
+ other route's `idempotencyConflict` changes meaning. A controller you inject through the `controller` prop
149
+ gets the same remedy. Both platforms.
150
+ - **Going Back from review and picking the same time no longer sends a second hold.** The time step took a
151
+ new hold on every pick, including the time the member already held. The platform claims a hold only on an
152
+ open slot, so it answered 409: the member read "That time was just taken. Pick another." about their own
153
+ hold, the chip stayed listed, and every press spent one of the member's hourly holds. A pick of the slot the
154
+ draft already holds, unexpired, now goes straight back to review with the same hold and booking key, as
155
+ consumer web does. After a real 409 on a hold the flow re-reads the times, so the one that went leaves the
156
+ grid. A booking refused as slot-taken (a taken slot, a lapsed hold, a visit that is not live, a reused key)
157
+ now also drops the draft's hold and key and re-reads the times, so the next pick, even of the same time, is
158
+ a fresh hold with a fresh key. Both platforms.
159
+ - **A hold refused for any reason but a conflict no longer says the time was taken.** A rate limit (429), a
160
+ server fault or a dropped connection on the hold showed "That time was just taken. Pick another." The time
161
+ step now shows the existing line "That did not book. Try again." for all three. A 402 on a hold now opens
162
+ the paused state, as a 402 at Confirm does. The time stays on the grid. No new strings. Both platforms.
163
+ - **A rate limit never shows the server's sentence: the booking steps and the visits list show their existing
164
+ lines.** A 429 at Confirm printed the platform's own sentence under Confirm, and since 0.5.1 a rate-limited
165
+ visits list printed it in its error card. That sentence carries an em-dash. By the owner's decision of
166
+ 2026-09-24, one rule everywhere, as consumer web does: the time step and the review step show the existing
167
+ line "That did not book. Try again." for a 429, and the visits list shows the card it shows for any other
168
+ failed read, "Could not load your visits" with "Check your connection and try again." (web), or the title
169
+ alone (React Native), each with its Try again button. The visits list still settles after one request, as
170
+ 0.5.1 made it. The review step's line keeps the test id `televet-review-rate-limited`.
171
+ `TelevetBookingFailure` still names the case `rate_limited`, and `televetBookingRefusal` still carries the
172
+ server's `message` for a host that reads it. No new strings. Both platforms.
173
+ - **Back from review keeps the day of the held time.** The day strip reopened on the first offered day, with
174
+ the member's own time out of sight. It now opens on the held slot's day and scrolls that day into the middle
175
+ of the strip, once, when the strip first draws it. Only the strip scrolls, never the page around it. Both
176
+ platforms.
177
+ - **A booking refused at Confirm keeps the member's day (web).** After a slot-taken refusal at Confirm (a taken
178
+ slot, a reused key, a visit that is not live) the time step reopened on the first offered day, because the
179
+ refusal clears the held slot the strip took its day from. It now reopens on the refused time's day while that
180
+ day still has times, and on the first offered day only when it has none, as consumer web does. The hold and
181
+ its booking key are still dropped, so the next pick is a fresh hold under a new key. No new strings.
182
+ - **The review states the visit length the platform serves.** The review's length row printed the SDK's
183
+ `CONSULT_MINUTES` (10) whatever the held slot said. It now reads the held slot's own interval
184
+ (`ends_at - starts_at`), which is the length the platform runs the visit for (15 minutes on the platform's
185
+ default grid since 2026-09-18, and 20, 30 or 60 for a clinician on longer slots) and the one consumer web's
186
+ review shows. It is capped at 60 minutes, as the platform caps it, and falls back to `CONSULT_MINUTES` only
187
+ for a slot that does not say. It is the only length served before a booking exists: `call_limits` arrives
188
+ with the join. Both platforms.
189
+ - **The calendar entry ends when the booked visit does (web).** The booked confirmation's calendar entry ended
190
+ `CONSULT_MINUTES` (10) after the start whatever the booked slot said, so on the platform's 15-minute default
191
+ grid every visit was written 5 minutes short, into the `.ics` file's `DTEND` and into the `endDate` a partner's
192
+ own `TelevetCalendarWriter` receives. It now ends after the booked slot's served length, the same one the
193
+ review states; consumer web ends its file at the booked slot's served end. `TelevetBookedScreen` takes the
194
+ length as an optional `lengthMinutes`, which the prebuilt flow passes; a screen mounted without it keeps
195
+ `CONSULT_MINUTES`. React Native's confirmation still uses the constant.
196
+ - **Leaving the in-page vet call returns to the visits instead of a blank dialog (web).** In 0.5.1, pressing the
197
+ lobby's own Leave left the visit dialog open, modal and blank, with no close control, and Escape did nothing;
198
+ the partner page behind it stayed blocked. `EverfurVetCall` removed Daily's Prebuilt iframe from the page
199
+ before awaiting `leave()`, and Daily's `leave()` only settles when that iframe answers, so it never settled,
200
+ `destroy()` never ran and `onLeave` never fired. Every Leave the SDK draws now keeps the frame in the page until
201
+ Daily has let go, waits at most 5 seconds for it, reports the departure once however often it is pressed, and
202
+ releases the Daily instance, so the member can join again without a reload. That includes a Leave that hit the
203
+ 5 second bound and a call the host unmounted mid-leave, which used to leave the instance registered (daily-js
204
+ then refused the next frame on the page), and a host that unmounts the call mid-leave is no longer called back
205
+ afterwards. After Daily's own Leave the instance is released at once as well, not only when the host unmounts
206
+ the call.
207
+ - **The in-page join asks the recording notice BEFORE it calls `POST /consults/{id}/join` (web).** That request
208
+ mints the token and records the member's arrival for the clinician, and the join screen sent it on mount, before
209
+ the notice, so a member who pressed "Not now" had already been logged as arriving. The order is now the
210
+ consumer web app's: the notice first, the request only from "Continue to the call" (the notice stays up,
211
+ controls disabled, while it travels), and "Not now" sends nothing. A page without the Daily module registered
212
+ says the visit cannot be joined before any notice or request. The acceptance covers one consult and one visit
213
+ into the room: a join screen handed another consult, or reached again after leaving, asks again before it
214
+ sends anything. The call surface does not ask a second time behind the join screen; that is an internal
215
+ receipt, not a prop, and `EverfurVetCall` still has no prop that skips the notice.
216
+ - **The web call is Daily's frame, with no SDK lobby (web).** `EverfurVetCall` no longer stacks the lobby panel
217
+ (and its second Leave and a hardcoded "10 MINUTE VISIT") under the frame, matching the consumer web app's owner
218
+ room: in the room, Daily's own tray carries Leave. Daily's rename control is off, as it is there. The frame's
219
+ height is capped at `calc(100dvh - 8rem)`, as the consumer web app caps its owner room, so in a wide container
220
+ Daily's tray (and its Leave) stays above the fold; before, a 16:9 frame at full width could be as tall as the
221
+ viewport. `clinicianName` and `petName` are still accepted and are not shown on the web.
222
+ `EverfurVetCallLobby` is still exported for a host that draws its own. The failure card and its Leave are
223
+ unchanged.
224
+ - **A way out before the room (web).** While the call was joining (Daily's frame loading, Daily's own pre-join
225
+ screen, which has a Join and no Leave, or a frame the page never let load) the member's only control was Join:
226
+ the dialog has no close control and Escape was swallowed. `EverfurVetCall` now draws its Leave under the frame
227
+ until Daily reports the member in the room, and removes it there.
228
+ - **Daily's pre-join fits the frame on a phone (web).** Daily's pre-join screen lays itself out by the frame's
229
+ shape, and the frame was 16:9 at every width, so at phone width Daily drew its side-by-side layout into 390 or
230
+ 358 px: Join and the heading ran about 34 px past the right edge and the camera and microphone toggles went off
231
+ the left edge (iPhone 14 emulation, Chromium and WebKit). On a phone in landscape the height cap cut the toggle
232
+ row by 43 px. Until the member is in the room, `EverfurVetCall` now sizes the frame by its own width: narrower
233
+ than 480 px it is portrait (3:4) and at least 500 px tall, so Daily stacks its pre-join; from 480 px it stays
234
+ 16:9 under the cap, with a floor of 45% of its width (at least 270 px) that only binds where the cap would cut
235
+ the toggle row, and the page scrolls there. In the room the frame is the 16:9 box under the cap, as before.
236
+ `scripts/measure-call-prejoin.mjs` measures it against Daily's real Prebuilt.
237
+ - **Escape on the in-page join screen is Leave everywhere except in the room (web).** It was swallowed for as
238
+ long as the call module was loaded: at Daily's pre-join screen, on the failure card and after the member had
239
+ left. It is now swallowed only between Daily's `joined-meeting` and the departure, so it never hangs up a call,
240
+ and while the join request travels, as the consumer web app holds its decline then.
241
+ - **Coming back to the visits puts focus on them (web).** Closing a booking started from the visits, declining
242
+ the recording notice, leaving the call or moving a visit popped the screen that held focus, and nothing on the
243
+ visits took it, so focus fell to `<body>` with the dialog still open: a screen reader said nothing. The
244
+ visits' first heading (Upcoming visits, else Past visits) now takes focus once the list has loaded, as the
245
+ booking steps' headings do, or the error notice when the list fails to load. It is `tabIndex={-1}`, so never a
246
+ tab stop. The flow's first screen still takes no focus from your page, and focus the member moved while the
247
+ list loaded stays where it is. `EverfurTelevet` tells the screen through its new `returnedTo` prop; a host
248
+ that mounts `TelevetVisitsScreen` in its own router can pass it the same way.
249
+ - **The recording notice takes focus when it appears (web).** Pressing Join on a visit replaced the visits with
250
+ the recording notice, and pressing Continue replaced the notice with the busy one the join request waits
251
+ behind. Each time the pressed control went with its screen and focus fell to `<body>` with the dialog still
252
+ open (the partner web call run of 2026-09-24, SDK main 62998c4). The notice's `alertdialog` now takes that
253
+ focus when it mounts, so it is announced by its title and Tab reaches Continue next, and the busy notice takes
254
+ it again while the request travels, when both its buttons are disabled. It is `tabIndex={-1}`, so never a tab
255
+ stop, and it takes focus only from `<body>`, never from a control the member is on. The same run found focus on
256
+ `<body>` after Not now, the pre-join Leave and Daily's own Leave; those returns land on the visits heading
257
+ (the entry above), now pinned through the whole call path.
258
+ - **Closing a dialog under React StrictMode returns focus to the control that opened it (web).** In development
259
+ StrictMode runs a mount effect twice with a simulated unmount between. The shared dialog hook restored focus
260
+ in that simulated unmount, where the page behind the still-modal dialog refused it, forgot the opener, and on
261
+ the re-run recorded the dialog itself as the opener; when the dialog really closed, focus fell to `<body>`.
262
+ The opener is now the control captured before `showModal()`, kept until focus actually lands on it. Every web
263
+ dialog shares the hook: the in-page vet visit, the chat sheets and the records sheets. That includes the visit
264
+ opened from inside `EverfurChat` (the urgency banner's Find a vet and the header's Talk to a vet), where the
265
+ partner web chat run of 2026-09-24 saw focus land on the page after the close. Production builds were not
266
+ affected.
267
+ - **`EverfurVetCallLobby` states the served visit length or none (web).** It defaulted to a hardcoded 10 minutes
268
+ (`VET_CALL_CONSULT_MINUTES`), which is wrong for any slot that is not 10 minutes long. Pass the join response's
269
+ `callLimits.consultMinutes`; without `consultMinutes` the duration line is not drawn.
270
+ - **React Native: `onLeave` fires once when the member leaves the vet call.** daily-js emits `left-meeting`
271
+ before `leave()` settles, so `EverfurVetCall` reported the departure from that event and again from the Leave
272
+ control, and a host that navigates on `onLeave` navigated twice. Every departure path now reports through one
273
+ guard, the consumer app's fix of 2026-09-13. On the member's own Leave the report waits for `leave()` and
274
+ `destroy()` to settle, so the surface stays mounted while Daily releases the camera and microphone, and that
275
+ wait is bounded at 5 seconds, so a `leave()` that never settles can no longer strand the member. A second
276
+ press while leaving is the same departure, and the recording notice's Leave reports once too. Nothing is
277
+ reported once the host has taken the surface down, so a departure that settles late, or a room the host
278
+ replaced while it was still leaving, cannot send a stale `onLeave` after the member has moved on.
279
+ - **React Native: the call lobby states the visit length the server sold.** The duration line always read
280
+ `VET_CALL_CONSULT_MINUTES` (10), while the join response's `call_limits.consult_minutes` was parsed and then
281
+ dropped. `TelevetJoinScreen` now passes the served value to the call, the lobby quotes it through the existing
282
+ `VET_CALL_COPY.lobbyDuration`, and the in-call clock turns to the danger colour at that same length, as the
283
+ consumer app does. With no served value, or one that is not a whole number of at least 1, both are unchanged.
284
+ One optional prop is added for this, and no existing signature changes: `EverfurVetCall` and
285
+ `EverfurVetCallLobby` (`@everfur/sdk/televet/call`) take `consultMinutes`, the length the join response served
286
+ (`VetCallCredential.callLimits?.consultMinutes`). A host that mounts the call itself can pass it; absent, the
287
+ lobby says what it said before.
288
+ - **React Native: the recording notice comes before `POST /consults/{id}/join`, and Leave on it sends nothing.**
289
+ That POST mints the room token and records the member's arrival for the clinician, and `TelevetJoinScreen`
290
+ sent it on mount, before the call surface showed "This consultation is recorded.", so a member who pressed
291
+ Leave on the notice had already been logged as arriving (the web join had the same order, known issue k4).
292
+ The join screen now shows the notice first and sends the join only on "Continue to the call", once per press.
293
+ While the request travels the notice stays up with Continue busy, and Leave still leaves, because iOS has no
294
+ back button; an answer that arrives after Leave opens nothing. The call surface is told the member already
295
+ accepted through an internal receipt for the room just minted, so it does not ask twice. There is no prop for
296
+ this: `EverfurVetCall` mounted by a host still asks on every mount, whatever props it is given. An app without
297
+ the Daily modules registered is told it cannot join before any notice or request, and the setup instruction
298
+ still reaches the console. No public signature changes.
299
+ - **React Native: an acceptance of the recording notice covers one consult and one visit into its room.**
300
+ `TelevetJoinScreen` handed another `consultId` while mounted now asks the notice again before that consult's
301
+ join is sent, and so does a consult it is handed back: an earlier Continue for a consult is spent once the
302
+ screen shows another, so returning to it never sends its join without a new press. When the call reports that the member left or the vet ended it, the join screen lets the room
303
+ go, so a host that keeps the screen mounted after `onLeave` shows the notice again, not a finished call that
304
+ still swallows the back press. `EverfurVetCall` handed another `roomUrl` while mounted is a fresh surface: it
305
+ asks the notice again before joining the new room, leaves and destroys the first, and carries no participant,
306
+ clock or reported departure across. No public signature changes.
307
+ - **React Native: the vet call is destroyed even when `leave()` never settles.** The teardown awaited `leave()`
308
+ before calling `destroy()`, so a `leave()` that hung meant `destroy()` was never called: the 5 second bound
309
+ reported the departure while the Daily call object stayed alive, possibly with the microphone and camera, and
310
+ a rejoin in the same session could throw "Duplicate DailyIframe instances are not allowed". `EverfurVetCall`
311
+ now waits for `leave()` for at most those 5 seconds, then, whether it settled, failed or timed out, turns the
312
+ microphone and camera off (`setLocalAudio(false)`, `setLocalVideo(false)`) and calls `destroy()`. `onLeave`
313
+ still fires once, after `destroy()` has been called. One limit remains in daily-js itself: its `destroy()`
314
+ begins with a `leave()` of its own, so a call object that never answers at all can still hold `destroy()`
315
+ open and ignore the device calls. No public signature changes.
316
+ - **React Native: the in-app vet call opens with the real `@daily-co/react-native-daily-js`.** In 0.5.0 and
317
+ 0.5.1 it never opened: every install registered as the docs say failed the SDK's shape check ("optional peer
318
+ '@daily-co/react-native-daily-js' is present but failed its shape check"). On the real package `default` is
319
+ the Daily class and `createCallObject` a static of it, and the check read `default` only when it was a plain
320
+ object, which is what its tests had stood in for it. It now reads a class default as well, calls
321
+ `createCallObject` on the class, and a contract suite holds it to the real package's own dist entry. No
322
+ public signature changes.
323
+ - **React Native: an app whose Daily module does not load is told so before the recording notice.** The join
324
+ screen asked only whether a module was registered, so a registered module that then failed to load showed the
325
+ notice, sent `POST /consults/{id}/join` (which stamps the member's arrival for the clinician) and only then
326
+ refused. It now asks the call's own guarded load first, and refuses with Leave before any notice or request.
327
+ - **React Native: the join screen's refusals stay inside the safe area and the screen gutter.** The SDK's modal
328
+ dropped its safe-area padding as soon as the call module loaded, so a call that then could not open drew its
329
+ refusal under the status bar. The padding now goes only when the room itself renders. The recording notice,
330
+ the refusals on the join screen and the reschedule screen now sit in the same side gutter as the visits and
331
+ the visit screens.
332
+ - **A records upload works on an Expo app (React Native).** On a partner's Expo SDK 57 app every records upload
333
+ failed and the member saw "Retry 1 that didn’t upload". It is the chat photo defect above:
334
+ `createRnRecordsUploadTransport` posted React Native's `{ uri, name, type }` FormData part through the global
335
+ `fetch`, which Expo replaces with `expo/fetch`, and its multipart encoder throws "Unsupported FormDataPart
336
+ implementation" on that part before any request exists. The records transport now posts through React Native's
337
+ own `XMLHttpRequest` too, and both transports share one request module, so records and chat make the same
338
+ request. The file still streams from disk, the policy fields still go first and the file last, no
339
+ cookie rides the request, and every failure still rejects with the same bounded message. `onProgress` still
340
+ reports completion only. The `fetch` option is unchanged and still wins when given.
341
+ - **A failed records upload row shows the file, "Failed" and why (React Native).** The row drew blank, with only
342
+ "Retry 1 that didn’t upload" under it. The file name, the status label and the reason shared one flex row, and
343
+ the reason, which could not shrink, kept the whole line and left the name and "Failed" 0 pt wide. The reason
344
+ now sits under the name and the label. The uploading row's progress bar, which sat in the same row the same
345
+ way, is under them too.
346
+ - **A refused records file is named with why, and never opens an empty request.** On the web upload card,
347
+ picking ONE file that is not a PDF, or ONE PDF over 25 MB, showed the connection line ("We couldn’t upload
348
+ that record. Check your connection and try again.") although nothing was sent, while the same files picked
349
+ together showed the skipped-files message. The card now shows the skipped-files message for whatever it
350
+ refused, however many files were picked. On both platforms the card created the owner request before it
351
+ looked at the pick, so a pick where every file was refused left an empty request on the member's page. The
352
+ card now classifies the pick first, by the same PDF and 25 MB rule the upload applies, creates the request
353
+ only when at least one file will upload, and hands only those files to `uploadDocuments`. A React Native
354
+ picker handle without `sizeBytes` is refused before any request exists, as the `RecordsDocumentPicker`
355
+ contract already said. Both platforms.
356
+ - **React Native: pull to refresh on the records request screen.** The consumer app re-reads a request when the
357
+ member pulls its request screen down; the SDK request screen had no way to re-read a status such as "Checked
358
+ by our team" short of leaving and coming back. `EverfurRecordsRequest` now carries a `RefreshControl` that
359
+ re-reads what the screen loaded. As in the app, the spinner shows for the member's pull only, until the re-read
360
+ settles, never for the status poll, and the screen keeps the request on screen while it re-reads instead of
361
+ going back to its skeleton. The request list (`EverfurRecordsRequests`) has no pull to refresh, as the
362
+ consumer app's request list has none. No new copy.
363
+
13
364
  ## [0.5.1] - 2026-09-23
14
365
 
15
366
  Fixes from the partner end-to-end run of 2026-09-23 (a Vite React web app and an Expo app, built through the
@@ -0,0 +1,54 @@
1
+ import { ReactNode } from 'react';
2
+ import { T as TelevetConsult } from './bookingTypes-DCs8mmBb.js';
3
+
4
+ /** The calendar entry a booked visit asks the host to write. */
5
+ interface TelevetCalendarEvent {
6
+ /**
7
+ * A stable key for THIS consult's entry.
8
+ *
9
+ * Keyed on the CONSULT so a re-press updates the same entry rather than writing a second event for one
10
+ * visit, and so a later cancel or reschedule can address the SAME row. A host that ignores the key writes
11
+ * duplicates the owner has to clean up by hand.
12
+ */
13
+ readonly key: string;
14
+ readonly title: string;
15
+ readonly startDate: Date;
16
+ readonly endDate: Date;
17
+ /** The reminder offset in minutes, negative for "before". The platform queues its own at the same offset. */
18
+ readonly alarmOffsetMinutes: number;
19
+ }
20
+ /**
21
+ * Writes the entry. Resolves true when it landed in a calendar; false when the host has no calendar to write to
22
+ * or the entry could not be written; `'handed-off'` when the entry was handed to the member to add themselves (a
23
+ * file download, as `downloadTelevetCalendarFile` does), which puts nothing in a calendar yet. On true the
24
+ * confirmation says the visit was added; on `'handed-off'` it keeps Add to Calendar, enabled, so the member can
25
+ * press it again, as the consumer web app's link can be.
26
+ */
27
+ type TelevetCalendarWriter = (event: TelevetCalendarEvent) => Promise<boolean | 'handed-off'>;
28
+ /** The calendar key for a consult. One definition, so a cancel and a reschedule address the booked row. */
29
+ declare function televetConsultCalendarKey(consultId: string): string;
30
+ interface TelevetBookedScreenProps {
31
+ readonly consult: TelevetConsult;
32
+ readonly petName: string | null;
33
+ readonly onDone: () => void;
34
+ /** Absent, there is no Add to calendar control. */
35
+ readonly onAddToCalendar?: TelevetCalendarWriter | undefined;
36
+ /**
37
+ * Has the host PROVEN it can host the call in place? Defaults to false.
38
+ *
39
+ * Fail closed: a host that has not said so is assumed not to have mounted the call surface, and the in-place
40
+ * join promise is withheld rather than printed on a hope.
41
+ */
42
+ readonly canJoinInApp?: boolean;
43
+ /**
44
+ * How long the visit is, in minutes: the booked slot's served length (`televetSlotMinutes`), which is the
45
+ * length the platform runs the visit for. The calendar entry ends this long after the start.
46
+ *
47
+ * Absent, or not a positive number, the entry falls back to `CONSULT_MINUTES`, the same fallback the review's
48
+ * length row uses for a slot that does not say. The prebuilt flow always passes the slot's length.
49
+ */
50
+ readonly lengthMinutes?: number | undefined;
51
+ }
52
+ declare function TelevetBookedScreen({ consult, petName, onDone, onAddToCalendar, canJoinInApp, lengthMinutes, }: TelevetBookedScreenProps): ReactNode;
53
+
54
+ export { type TelevetCalendarWriter as T, type TelevetCalendarEvent as a, TelevetBookedScreen as b, type TelevetBookedScreenProps as c, televetConsultCalendarKey as t };
@@ -0,0 +1,54 @@
1
+ import { ReactNode } from 'react';
2
+ import { T as TelevetConsult } from './bookingTypes-DCs8mmBb.cjs';
3
+
4
+ /** The calendar entry a booked visit asks the host to write. */
5
+ interface TelevetCalendarEvent {
6
+ /**
7
+ * A stable key for THIS consult's entry.
8
+ *
9
+ * Keyed on the CONSULT so a re-press updates the same entry rather than writing a second event for one
10
+ * visit, and so a later cancel or reschedule can address the SAME row. A host that ignores the key writes
11
+ * duplicates the owner has to clean up by hand.
12
+ */
13
+ readonly key: string;
14
+ readonly title: string;
15
+ readonly startDate: Date;
16
+ readonly endDate: Date;
17
+ /** The reminder offset in minutes, negative for "before". The platform queues its own at the same offset. */
18
+ readonly alarmOffsetMinutes: number;
19
+ }
20
+ /**
21
+ * Writes the entry. Resolves true when it landed in a calendar; false when the host has no calendar to write to
22
+ * or the entry could not be written; `'handed-off'` when the entry was handed to the member to add themselves (a
23
+ * file download, as `downloadTelevetCalendarFile` does), which puts nothing in a calendar yet. On true the
24
+ * confirmation says the visit was added; on `'handed-off'` it keeps Add to Calendar, enabled, so the member can
25
+ * press it again, as the consumer web app's link can be.
26
+ */
27
+ type TelevetCalendarWriter = (event: TelevetCalendarEvent) => Promise<boolean | 'handed-off'>;
28
+ /** The calendar key for a consult. One definition, so a cancel and a reschedule address the booked row. */
29
+ declare function televetConsultCalendarKey(consultId: string): string;
30
+ interface TelevetBookedScreenProps {
31
+ readonly consult: TelevetConsult;
32
+ readonly petName: string | null;
33
+ readonly onDone: () => void;
34
+ /** Absent, there is no Add to calendar control. */
35
+ readonly onAddToCalendar?: TelevetCalendarWriter | undefined;
36
+ /**
37
+ * Has the host PROVEN it can host the call in place? Defaults to false.
38
+ *
39
+ * Fail closed: a host that has not said so is assumed not to have mounted the call surface, and the in-place
40
+ * join promise is withheld rather than printed on a hope.
41
+ */
42
+ readonly canJoinInApp?: boolean;
43
+ /**
44
+ * How long the visit is, in minutes: the booked slot's served length (`televetSlotMinutes`), which is the
45
+ * length the platform runs the visit for. The calendar entry ends this long after the start.
46
+ *
47
+ * Absent, or not a positive number, the entry falls back to `CONSULT_MINUTES`, the same fallback the review's
48
+ * length row uses for a slot that does not say. The prebuilt flow always passes the slot's length.
49
+ */
50
+ readonly lengthMinutes?: number | undefined;
51
+ }
52
+ declare function TelevetBookedScreen({ consult, petName, onDone, onAddToCalendar, canJoinInApp, lengthMinutes, }: TelevetBookedScreenProps): ReactNode;
53
+
54
+ export { type TelevetCalendarWriter as T, type TelevetCalendarEvent as a, TelevetBookedScreen as b, type TelevetBookedScreenProps as c, televetConsultCalendarKey as t };
@@ -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-C0m1G1fS.js';
6
- import { W as WidgetPet, P as PetProfileInput } from './petsRepository-DhsX3GLw.js';
7
- import { U as UrgencyDisplayLevel, L as LifecyclePort, E as EverfurChatDraftStore } from './attachments-CzZ7in24.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';
8
8
 
9
9
  /** Where a picked image comes from: the host's photo library, or its camera. */
10
10
  type AttachmentSource = 'photo' | 'camera';
@@ -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-DNXyyzZF.cjs';
6
- import { W as WidgetPet, P as PetProfileInput } from './petsRepository-D1bnxDZg.cjs';
7
- import { U as UrgencyDisplayLevel, L as LifecyclePort, E as EverfurChatDraftStore } from './attachments-CwsR-bPN.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';
8
8
 
9
9
  /** Where a picked image comes from: the host's photo library, or its camera. */
10
10
  type AttachmentSource = 'photo' | 'camera';
@@ -1,5 +1,5 @@
1
1
  import { CSSProperties, ReactNode } from 'react';
2
- import { f as RecordsScreenBaseProps } from './useRecordsDepth--ObtMyjH.js';
2
+ import { f as RecordsScreenBaseProps } from './useRecordsDepth-CG63cXFZ.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-B35y8bV-.cjs';
2
+ import { f as RecordsScreenBaseProps } from './useRecordsDepth-ChhS4c6r.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-B35y8bV-.cjs';
3
+ import { f as RecordsScreenBaseProps } from './useRecordsDepth-ChhS4c6r.cjs';
4
4
 
5
5
  interface EverfurRecordsDepthProps extends Pick<RecordsScreenBaseProps, 'petRef' | 'petName' | 'onNavigate' | 'onBack'> {
6
6
  readonly style?: StyleProp<ViewStyle>;
@@ -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--ObtMyjH.js';
3
+ import { f as RecordsScreenBaseProps } from './useRecordsDepth-CG63cXFZ.js';
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-DjqhDm5Z.cjs';
5
- export { I as IdentityProvider, n as newSessionId } from '../identity-DjqhDm5Z.cjs';
4
+ import { A as AuthContext } from '../identity-Z0yj-fC_.cjs';
5
+ export { I as IdentityProvider, n as newSessionId } from '../identity-Z0yj-fC_.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-6qS7Sohy.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';
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-Dy_L7Yq4.js';
5
- export { I as IdentityProvider, n as newSessionId } from '../identity-Dy_L7Yq4.js';
4
+ import { A as AuthContext } from '../identity-AuoeDRTh.js';
5
+ export { I as IdentityProvider, n as newSessionId } from '../identity-AuoeDRTh.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-X6BtwDwQ.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';
17
17
 
18
18
  /**
19
19
  * The full /state payload. `degraded` + `cache_epoch` + `cache_key` are additive fields an older backend
@@ -1,7 +1,7 @@
1
1
  import { E as EverfurError } from './EverfurError-DmELDpXp.js';
2
- import { R as RequestFunnel } from './requestFunnel-FvE5yLDD.js';
2
+ import { R as RequestFunnel } from './requestFunnel-CJC_q4BZ.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-C0m1G1fS.js';
4
+ import { R as ReadableStore, T as Timers } from './ports-DNIvfzBP.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,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-DNXyyzZF.cjs';
5
- import { C as CheckinAnswer, a as CheckinOutcome } from './casesRepository-aryld7fJ.cjs';
6
- import { i as UrgencyLevel } from './config-6qS7Sohy.cjs';
7
- import { W as WidgetPet } from './petsRepository-D1bnxDZg.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';
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-Nk73Lll8.cjs';
9
+ import { c as AttachmentQueue, d as AttachmentState, A as AttachmentError, b as AttachmentOutcome } from './attachments-affGN_Uv.cjs';
10
10
 
11
11
  /** The three thumbs. `'neutral'` is the app's rotated thumbs-down glyph. */
12
12
  type ChatSentiment = 'up' | 'neutral' | 'down';