@everfur/sdk 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +311 -0
- package/README.md +24 -16
- package/dist/{Chat-CFi5cNUF.d.ts → Chat-Cv9EYMYA.d.ts} +3 -3
- package/dist/{Chat-DQOsAumH.d.cts → Chat-s2vcPbw3.d.cts} +3 -3
- package/dist/{DepthViews-uilL1NJZ.d.ts → DepthViews-CVxUHOJ-.d.ts} +1 -1
- package/dist/{DepthViews-C409ubw-.d.cts → DepthViews-Cz7mSW7p.d.cts} +1 -1
- package/dist/{DepthViews-es8mIK0w.d.ts → DepthViews-DNRY0Ugm.d.ts} +1 -1
- package/dist/{DepthViews-D81c95Ra.d.cts → DepthViews-DvNixbxg.d.cts} +1 -1
- package/dist/{ErrorPolicyPort-D22LUVrK.d.ts → ErrorPolicyPort-DwSnmGcX.d.ts} +1 -1
- package/dist/{ErrorPolicyPort-D6CybiqJ.d.cts → ErrorPolicyPort-NFDPe-5w.d.cts} +1 -1
- package/dist/{EverfurError-DmELDpXp.d.cts → EverfurError-pEXDbUJ1.d.cts} +9 -1
- package/dist/{EverfurError-DmELDpXp.d.ts → EverfurError-pEXDbUJ1.d.ts} +9 -1
- package/dist/{EverfurResult-ZtH-YV1h.d.cts → EverfurResult-dtl8ERAw.d.cts} +1 -1
- package/dist/{EverfurResult-SGvHKsCB.d.ts → EverfurResult-sKkoXew0.d.ts} +1 -1
- package/dist/IntakeMedia-BFZfBLjB.d.cts +116 -0
- package/dist/{IntakeMedia-BcjjUizr.d.ts → IntakeMedia-BH0fYDT2.d.cts} +19 -2
- package/dist/{IntakeMedia-CL_S2tZE.d.cts → IntakeMedia-Bavl7E4Z.d.ts} +19 -2
- package/dist/IntakeMedia-C8GlN_GK.d.ts +116 -0
- package/dist/animations/index.cjs +109 -1
- package/dist/animations/index.d.cts +6 -6
- package/dist/animations/index.d.ts +6 -6
- package/dist/animations/index.js +109 -1
- package/dist/{attachments-DNaSbmA3.d.ts → attachments-BaslXMdS.d.ts} +3 -3
- package/dist/{attachments-DI710no4.d.cts → attachments-DbFwz4iq.d.cts} +7 -7
- package/dist/{attachments-B2GjBgvF.d.cts → attachments-y3EcOX_w.d.cts} +3 -3
- package/dist/{attachments-SF88N654.d.ts → attachments-y_aO4hlk.d.ts} +7 -7
- package/dist/{bookingCopy-xlXxORCA.d.cts → bookingCopy-4i1R7klo.d.cts} +15 -7
- package/dist/{bookingCopy-D2N3RhsV.d.ts → bookingCopy-B3UMITD-.d.ts} +15 -7
- package/dist/{branding-ytH4jJq1.d.ts → branding-BYEqU855.d.ts} +1 -1
- package/dist/{branding-Bio5MM6g.d.cts → branding-BwEu68C8.d.cts} +1 -1
- package/dist/{callRoom-7wrE3RP2.d.ts → callRoom-B1qq_D-4.d.ts} +1 -1
- package/dist/{callRoom-B_EqaUzx.d.cts → callRoom-DfZTWFXh.d.cts} +1 -1
- package/dist/{casesRepository-B-md8h1S.d.cts → casesRepository-B6Z0XBIb.d.cts} +2 -2
- package/dist/{casesRepository-CC6951TK.d.ts → casesRepository-SI0zp5YH.d.ts} +2 -2
- package/dist/chat/index.cjs +333 -1
- package/dist/chat/index.d.cts +12 -12
- package/dist/chat/index.d.ts +12 -12
- package/dist/chat/index.js +333 -1
- package/dist/client/index.cjs +115 -11
- package/dist/client/index.d.cts +14 -14
- package/dist/client/index.d.ts +14 -14
- package/dist/client/index.js +115 -11
- package/dist/{config-BM0sv5jx.d.ts → config-Cvo47voG.d.ts} +3 -3
- package/dist/{config-Daa1plhQ.d.cts → config-v5k6NsK_.d.cts} +3 -3
- package/dist/consent/index.cjs +112 -1
- package/dist/consent/index.d.cts +9 -9
- package/dist/consent/index.d.ts +9 -9
- package/dist/consent/index.js +112 -1
- package/dist/{context-DQHtwcHN.d.cts → context-BZOtJbBO.d.cts} +9 -9
- package/dist/{context-CDV2pwGI.d.ts → context-cZ7NZ0sG.d.ts} +9 -9
- package/dist/{copy-Cl1l-qmY.d.ts → copy-CZBUHbyS.d.ts} +5 -5
- package/dist/{copy-BEx5F71o.d.cts → copy-ChJupm1p.d.cts} +5 -5
- package/dist/core/index.cjs +140 -13
- package/dist/core/index.d.cts +19 -19
- package/dist/core/index.d.ts +19 -19
- package/dist/core/index.js +140 -13
- package/dist/{depth-CAAwiStc.d.cts → depth-CRyU7-pf.d.cts} +7 -6
- package/dist/{depth-DfNRVtrV.d.ts → depth-vNpwOkZ8.d.ts} +7 -6
- package/dist/{entitlementRepository-DPvWjV-N.d.cts → entitlementRepository-Bv8nSYRd.d.cts} +4 -4
- package/dist/{entitlementRepository-CVWvND1P.d.ts → entitlementRepository-CUdxbQrP.d.ts} +4 -4
- package/dist/{identity-BhT7oJDS.d.ts → identity-B8GkH53J.d.ts} +2 -2
- package/dist/{identity-DUlshgow.d.cts → identity-BHaMJ3KK.d.cts} +2 -2
- package/dist/index.cjs +363 -12
- package/dist/index.d.cts +19 -19
- package/dist/index.d.ts +19 -19
- package/dist/index.js +363 -12
- package/dist/{models-DSqA5Ahs.d.ts → models-CrbUwbuS.d.ts} +1 -1
- package/dist/{models-Bn2z0NNo.d.cts → models-DzvqEjMm.d.cts} +1 -1
- package/dist/notifications/index.cjs +94 -2
- package/dist/notifications/index.d.cts +5 -5
- package/dist/notifications/index.d.ts +5 -5
- package/dist/notifications/index.js +94 -2
- package/dist/{pets-Bneph9m1.d.cts → pets-BI7Ji8hi.d.cts} +2 -2
- package/dist/{pets-DjcLmRrW.d.ts → pets-Dnn-DK1T.d.ts} +2 -2
- package/dist/{petsRepository-BwxmfX_s.d.ts → petsRepository-B2lO1sKS.d.ts} +2 -2
- package/dist/{petsRepository-BnVre3q7.d.cts → petsRepository-BP9nhM1R.d.cts} +2 -2
- package/dist/photo/index.cjs +131 -1
- package/dist/photo/index.d.cts +3 -3
- package/dist/photo/index.d.ts +3 -3
- package/dist/photo/index.js +131 -1
- package/dist/{ports-Di9rdLO9.d.ts → ports-BUXF0Rp3.d.ts} +7 -7
- package/dist/{ports-BPKxFOSe.d.cts → ports-C40S2V14.d.cts} +7 -7
- package/dist/records/depth/index.cjs +200 -2
- package/dist/records/depth/index.d.cts +11 -11
- package/dist/records/depth/index.d.ts +11 -11
- package/dist/records/depth/index.js +200 -2
- package/dist/records/index.cjs +742 -4
- package/dist/records/index.d.cts +22 -14
- package/dist/records/index.d.ts +22 -14
- package/dist/records/index.js +742 -4
- package/dist/{requestFunnel-CiSX2pun.d.ts → requestFunnel-BrOijMcw.d.ts} +5 -5
- package/dist/{requestFunnel-DMR_1T3O.d.cts → requestFunnel-BxNVGyGm.d.cts} +5 -5
- package/dist/{runtimeTypes-CkMFCbHW.d.ts → runtimeTypes-CvI1ZH8j.d.ts} +7 -7
- package/dist/{runtimeTypes-2gJ5hYoE.d.cts → runtimeTypes-DOO3f-W-.d.cts} +7 -7
- package/dist/server/events/index.cjs +92 -2
- package/dist/server/events/index.d.cts +4 -4
- package/dist/server/events/index.d.ts +4 -4
- package/dist/server/events/index.js +92 -2
- package/dist/server/index.cjs +95 -2
- package/dist/server/index.d.cts +5 -5
- package/dist/server/index.d.ts +5 -5
- package/dist/server/index.js +95 -2
- package/dist/televet/booking/index.cjs +394 -4
- package/dist/televet/booking/index.d.cts +24 -127
- package/dist/televet/booking/index.d.ts +24 -127
- package/dist/televet/booking/index.js +394 -4
- package/dist/televet/call/index.cjs +71 -1
- package/dist/televet/call/index.d.cts +3 -3
- package/dist/televet/call/index.d.ts +3 -3
- package/dist/televet/call/index.js +71 -1
- package/dist/televet/index.cjs +101 -1
- package/dist/televet/index.d.cts +25 -8
- package/dist/televet/index.d.ts +25 -8
- package/dist/televet/index.js +101 -1
- package/dist/{bookingTypes-D4pD3tHs.d.ts → televetGlobal-C5dTUACe.d.cts} +34 -1
- package/dist/{bookingTypes-D4pD3tHs.d.cts → televetGlobal-CnjrfhT4.d.ts} +34 -1
- package/dist/testing/index.cjs +98 -7
- package/dist/testing/index.d.cts +3 -3
- package/dist/testing/index.d.ts +3 -3
- package/dist/testing/index.js +98 -7
- package/dist/testing/rn/index.cjs +101 -1
- package/dist/testing/rn/index.d.cts +13 -13
- package/dist/testing/rn/index.d.ts +13 -13
- package/dist/testing/rn/index.js +101 -1
- package/dist/testing/web/index.cjs +95 -1
- package/dist/testing/web/index.d.cts +14 -14
- package/dist/testing/web/index.d.ts +14 -14
- package/dist/testing/web/index.js +95 -1
- package/dist/{timelineRows-D3171ksh.d.ts → timelineRows-CEx2TzYB.d.ts} +1 -1
- package/dist/{timelineRows-DDnz4gx2.d.cts → timelineRows-Dffceql_.d.cts} +1 -1
- package/dist/{tokens-BYtbKwFh.d.cts → tokens-Tzo1KBND.d.cts} +43 -2
- package/dist/{tokens-BYtbKwFh.d.ts → tokens-Tzo1KBND.d.ts} +43 -2
- package/dist/{typeStyle-DKz02DGU.d.ts → typeStyle-C3IG9Sft.d.ts} +22 -3
- package/dist/{typeStyle-BqfwxZH4.d.cts → typeStyle-CZ3Tj60N.d.cts} +22 -3
- package/dist/{useRecordsDepth-COJ0wSYK.d.cts → useRecordsDepth-D6a6-_u_.d.cts} +7 -6
- package/dist/{useRecordsDepth-Z9FF5X4E.d.ts → useRecordsDepth-DuPUJFDQ.d.ts} +7 -6
- package/dist/{useVetVisit-Ds8f1peV.d.cts → useVetVisit-BQX_aTJs.d.cts} +2 -2
- package/dist/{useVetVisit-BsjoDO-n.d.ts → useVetVisit-RPBOabl8.d.ts} +2 -2
- package/dist/video/index.cjs +122 -1
- package/dist/video/index.d.cts +3 -3
- package/dist/video/index.d.ts +3 -3
- package/dist/video/index.js +122 -1
- package/dist/{view-DoQEHXJR.d.ts → view-BSmN5RWf.d.ts} +5 -5
- package/dist/{view-nRy6SitU.d.cts → view-UFwCG1Pn.d.cts} +5 -5
- package/dist/web/consent/index.cjs +111 -1
- package/dist/web/consent/index.d.cts +9 -9
- package/dist/web/consent/index.d.ts +9 -9
- package/dist/web/consent/index.js +111 -1
- package/dist/web/index.cjs +325 -14
- package/dist/web/index.d.cts +22 -22
- package/dist/web/index.d.ts +22 -22
- package/dist/web/index.js +325 -14
- package/dist/web/notifications/index.cjs +95 -2
- package/dist/web/notifications/index.d.cts +7 -7
- package/dist/web/notifications/index.d.ts +7 -7
- package/dist/web/notifications/index.js +95 -2
- package/dist/web/records/depth/index.cjs +169 -2
- package/dist/web/records/depth/index.d.cts +11 -11
- package/dist/web/records/depth/index.d.ts +11 -11
- package/dist/web/records/depth/index.js +169 -2
- package/dist/web/records/index.cjs +689 -4
- package/dist/web/records/index.d.cts +34 -37
- package/dist/web/records/index.d.ts +34 -37
- package/dist/web/records/index.js +689 -4
- package/dist/web/records/upload/index.cjs +3 -0
- package/dist/web/records/upload/index.d.cts +23 -0
- package/dist/web/records/upload/index.d.ts +23 -0
- package/dist/web/records/upload/index.js +3 -0
- package/dist/web/televet/booking/index.cjs +423 -4
- package/dist/web/televet/booking/index.d.cts +21 -35
- package/dist/web/televet/booking/index.d.ts +21 -35
- package/dist/web/televet/booking/index.js +423 -4
- package/dist/web/televet/call/index.cjs +28 -1
- package/dist/web/televet/call/index.d.cts +2 -2
- package/dist/web/televet/call/index.d.ts +2 -2
- package/dist/web/televet/call/index.js +28 -1
- package/dist/web/televet/index.cjs +102 -4
- package/dist/web/televet/index.d.cts +22 -11
- package/dist/web/televet/index.d.ts +22 -11
- package/dist/web/televet/index.js +102 -4
- package/package.json +20 -4
- package/web/records/upload/native-blocked.cjs +12 -0
- package/web/records/upload/package.json +8 -0
- package/dist/televetGlobal-CEk0_cW9.d.cts +0 -34
- package/dist/televetGlobal-Cs9Wbqsy.d.ts +0 -34
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,317 @@ changes that has not been earned by any production integration yet.
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.8.0] - 2026-10-02
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **`identitySignatureRequired` is a named error code.** The pets routes' 403 for a health field on a session your
|
|
18
|
+
backend did not sign (`registerPet` or `updatePet` with health fields, or `getPetProfile`, while anonymous)
|
|
19
|
+
settled as `unknown`. It is now `identitySignatureRequired` (category `authentication`, not retryable), shown to an
|
|
20
|
+
end user with the existing access-denied sentence. Documented in 11-ERRORS. (#155)
|
|
21
|
+
- **`EverfurError.retryAfterSeconds`.** The wait a refusal named, in seconds: the body's `retry_after_seconds`, or a
|
|
22
|
+
`Retry-After` header the transport folds into it. (#155)
|
|
23
|
+
- **`@everfur/sdk/web/records/upload`**: `createWebRecordsUploadTransport` on its own entry, for the `uploadTransport`
|
|
24
|
+
on a provider config at the app root (1_295 bytes minified, against 85_208 for the same import from
|
|
25
|
+
`@everfur/sdk/web/records`, and 388_582 before the tree-shaking change under Changed). `@everfur/sdk/web/records` still exports it. (#158)
|
|
26
|
+
- **`TelevetBookingController.releaseHold(slotId)`** (optional on the interface, so a host's own controller
|
|
27
|
+
still type-checks): `DELETE /widget/v1/televet/slots/{slot_id}/hold`, served by EFBackend from #1865. The
|
|
28
|
+
server releases only this member's own hold and answers the same either way. A server without the route (a
|
|
29
|
+
dark 404) settles `ok`, and the hold lapses as before. (#144)
|
|
30
|
+
- Colour tokens `highlight`, `onHighlight`, `mark`, `controlEdge`, `structureEdge` and `primaryEdge` on
|
|
31
|
+
`EverfurColorTokens`; `themeAdjustments` and `EverfurThemeAdjustment`; the `SlotSurrounds` type the slot guard now
|
|
32
|
+
takes; and `ThemeSeams.isDevelopment`. Additive: the appearance contract
|
|
33
|
+
(`contract/appearance/appearance_v1.json`) and the branding wire are unchanged. (#145)
|
|
34
|
+
- **`consentText`: the records consent gate shows the partner's own consent text (owner decision 2026-10-02).**
|
|
35
|
+
Every records surface that takes `consentVersion` now takes `consentText` beside it (React Native:
|
|
36
|
+
`EverfurRecordsFlow`, `EverfurRecordsDashboard`, `EverfurRecordDetail`, `EverfurRecordDocument`; web: the same
|
|
37
|
+
plus `EverfurRecordsPage`; the iframe embed: the `consentText` init option and `data-consent-text`, an additive
|
|
38
|
+
`ef:init` field). The gate renders it as plain text, line breaks kept, above `Allow`, which stays its own button.
|
|
39
|
+
The visible `DRAFT COPY` line that held its place is gone: Everfur supplies no consent wording, so a surface
|
|
40
|
+
given `consentVersion` without `consentText` shows only `Consent required` and `Allow`, and a development build
|
|
41
|
+
logs one `[everfur]` console warning naming the missing prop. Optional in the types (a minor addition), because
|
|
42
|
+
`consentVersion` is optional too. Documented in 09-GUIDE-RECORDS-AND-CONSENT, 05-SDK-INTEGRATION,
|
|
43
|
+
13-WEB-INTEGRATION and the quickstart; the examples pass both. The Everfur CLI's docs mirror resyncs itself. (#168)
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **A partner's bundler can now tree-shake the web entries.** Importing one thing from `@everfur/sdk/web` used to
|
|
48
|
+
cost the whole entry: a page importing only `petRef` carried 354_011 minified bytes, exactly what `EverfurChat`
|
|
49
|
+
costs, because the build kept function names with a call after every function and its minifier deleted every
|
|
50
|
+
`/* @__PURE__ */` annotation. The published files are now minified without renaming anything and keep their pure
|
|
51
|
+
annotations, so a page that imports `petRef` alone carries 29_035 bytes (esbuild, minified), and the partner demo's
|
|
52
|
+
app root went from 686_156 to 203_196. Function and component names are unchanged in stack traces and React
|
|
53
|
+
DevTools. `tests/supply-chain/consumerTreeShake.test.ts` keeps it that way. (#158)
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
|
|
57
|
+
#### Packaging and client
|
|
58
|
+
|
|
59
|
+
- **Importing `@everfur/sdk/web` from a Next.js server component works.** It failed with the cryptic
|
|
60
|
+
`(0, c.createContext) is not a function`: the provider and hooks ran in the server graph. Every `web/*` entry and
|
|
61
|
+
`testing/web` now opens with `"use client"` (`scripts/add-use-client.mjs`, run by `build`), as React libraries
|
|
62
|
+
mark their client entries; `./server`, `./client`, `./core` and the React Native entries do not. Server rendering
|
|
63
|
+
of the client components is unchanged (pinned by rendering the built provider with `renderToString`). (#163)
|
|
64
|
+
- **One session mint per page load, not three.** Every runtime's identity called your `getToken` for itself, so
|
|
65
|
+
React StrictMode's second runtime (and any second `EverfurProvider`) minted again in parallel: the production
|
|
66
|
+
integration test of 0.7.1 counted three `POST` session mints on every load. Concurrent mints for the same
|
|
67
|
+
tenant (publishable key and API base URL) and the same user now share one `getToken` call. Only the call in
|
|
68
|
+
flight is shared: the next mint after it settles calls `getToken` again, so the expiry your minter names and
|
|
69
|
+
a sign-out mean what they did. (#140)
|
|
70
|
+
- **A pet is registered once per burst, not once per caller.** A sample app counted every pet registered twice per
|
|
71
|
+
navigation: React StrictMode runs the chat's `pet` registration twice, and a host registering on load sends the
|
|
72
|
+
same body beside it. Concurrent identical `registerPet` calls (same tenant, identity, pet and body, from one
|
|
73
|
+
runtime or several, and from the chat's `pet` prop) now share one `POST /widget/v1/pets/{pet_ref}` and its
|
|
74
|
+
result, as session mints do (#140). Only the request in flight is shared; the next call sends again. (#160)
|
|
75
|
+
- **The birth date rule is documented.** The same report met `validationFailed` with no request id on
|
|
76
|
+
`birthDate: '2026-09-01'`. A recent date, today's included, is accepted (now pinned by a test); the refusal a
|
|
77
|
+
birth date meets before any request is the pair rule (a birth date always goes with `birthDatePrecision`), which
|
|
78
|
+
was documented nowhere. 09-GUIDE now states it and where the reason is (`error.wireMessage`). (#160)
|
|
79
|
+
- **The anonymous-session budget's 429 is told apart.** A new anonymous identity per page load (the session id is
|
|
80
|
+
memory-only) meets the API's budget of 20 new anonymous identities per IP per 5 minutes, and the 429 read as a
|
|
81
|
+
send throttle. The API now sends `Retry-After` and `reason_code: anonymous_session_limit` on it; the SDK settles
|
|
82
|
+
it as `rateLimited` with that `reasonCode` and `retryAfterSeconds`, and (through the header fold) does not retry
|
|
83
|
+
into a wait longer than its retry budget. Documented in 10-TESTING and 11-ERRORS. (#155)
|
|
84
|
+
|
|
85
|
+
#### Theme
|
|
86
|
+
|
|
87
|
+
- **Everfur's colours no longer leak through a partner palette (web and React Native).** A production integration
|
|
88
|
+
test of 0.7.1 themed through `appearance` (teal primary, warm white background) still showed Everfur's mint pet
|
|
89
|
+
avatar, the pastel green `Good to know` banner, a pink picked day chip and `Now` pill, and a green current step on
|
|
90
|
+
the request timeline. Each now paints from the theme: the picked chips, the `Now` pill, the open history row and a
|
|
91
|
+
pet's initials disc from the new `highlight` / `onHighlight` tokens (a tint of the partner's accent, else its
|
|
92
|
+
primary), the avatar ring and the current step from `mark`, and the `Good to know` and `Monitor at home` banners
|
|
93
|
+
from the brand's wash, outline and ink (`Vet care suggested soon` from the palette's caution tokens). Everfur's
|
|
94
|
+
own light look is unchanged: every new token is the compiled value it stands in for, and the banners and the
|
|
95
|
+
initials disc keep the app's authored paint there. (#145)
|
|
96
|
+
- **The dark palette is dark-aware everywhere (`night` preset).** The calm urgency banners were self-contained
|
|
97
|
+
pastel chips (THEME-01), a glaring mint or amber slab on the dark ground, and the picked chip and `Now` pill stayed
|
|
98
|
+
light pink. They now take the dark palette's tokens: a dark wash under light ink, and the pink's dusk twin
|
|
99
|
+
(`#605051`) as the dark `highlight`. (#145)
|
|
100
|
+
- **Non-text contrast (WCAG 1.4.11).** The text guard is joined by a guard on the outlines that show a control or a
|
|
101
|
+
structural mark: the new `controlEdge` (outlined pills, such as the chat header's Vet entry), `structureEdge` (the
|
|
102
|
+
steps still to come on a request timeline and its rail, an informational banner's edge) and `mark` reach 3:1 on a
|
|
103
|
+
partner palette, and `primaryEdge` edges the send disc, the owner's bubble and a done step when a pale primary does
|
|
104
|
+
not stand out from its ground. A slot with an opaque fill that melts into the ground around it (a white
|
|
105
|
+
`userBubble` on a white chat) is given a `borderColor` that reads at 3:1 there, and a partner's own `borderColor`
|
|
106
|
+
on such a slot is pushed until it does. Everfur's compiled palettes keep their authored hairlines. (#145)
|
|
107
|
+
- **The guard no longer changes a partner's colours in silence.** `themeAdjustments(input)` lists every partner
|
|
108
|
+
colour the contrast guard replaced (field, role, requested, painted), and the provider writes that list to the
|
|
109
|
+
console once in a development build (`__DEV__` on React Native, the bundler's `process.env.NODE_ENV` on the web). (#145)
|
|
110
|
+
- **A partner brand that already reads keeps its outline's hue (teal `#0F766E`).** The non-text guard pushed an
|
|
111
|
+
outline that did not read (the brand's 20% hairline) toward black or white, so a teal partner's outlined pills
|
|
112
|
+
(the chat header's Vet entry among them) were painted grey and a development build reported
|
|
113
|
+
`primary_color (control outline): #0F766E -> #7C8683`, although the teal reads at 3:1 on its warm white and on
|
|
114
|
+
white. When the brand itself reads at 3:1 on every control ground the outline is now strengthened toward the brand
|
|
115
|
+
(`#49968E` on `#FFFBF5`) and nothing is reported; a brand that does not read is pushed and reported as before. (#170)
|
|
116
|
+
|
|
117
|
+
#### Records
|
|
118
|
+
|
|
119
|
+
- **A refused signature upload no longer fails the clinic request (web and React Native).** A production sandbox
|
|
120
|
+
test found the signature upload's direct S3 POST refused from a partner's page (the prod bucket's CORS listed only
|
|
121
|
+
Everfur origins), so every web clinic request failed at signing. When that POST itself fails (a network or CORS
|
|
122
|
+
refusal, or S3 answering non-2xx) the built-in pad now sends the same PNG inline as `signature_png_base64`, which
|
|
123
|
+
the create API has always accepted. A refused initiate, an untrusted policy host, a failed complete and a cancel
|
|
124
|
+
still fail as before. Web: the pad gains `capture(controller, signal)`, which the built-in forms use;
|
|
125
|
+
`register` is unchanged and still strict. React Native: the multi-clinic composer keeps the inline PNG when its
|
|
126
|
+
registration's S3 POST fails. (#162)
|
|
127
|
+
- **The clinic-release consent screen no longer reads "I, , authorize ..." (web and React Native).** The served
|
|
128
|
+
authorization sentence is the owner's own signed scope, and with no name typed yet it rendered with a blank
|
|
129
|
+
where the name goes. It now waits for a name: nothing stands in for it while the field is empty, and it
|
|
130
|
+
appears as the name is typed. Found by the production integration test of 0.7.1. (#139)
|
|
131
|
+
- **"Please add your signature before submitting." no longer greets the consent screen (web).** #127 moved the
|
|
132
|
+
line on the in-place clinic request forms; the consent screen the picker opens still showed it as soon as a
|
|
133
|
+
clinic was chosen. It now appears only when the signature is the one thing keeping `Send request` closed. (#139)
|
|
134
|
+
- **The React Native multi-clinic composer scrolls, closes and sends (React Native).** On an iPhone 17 simulator
|
|
135
|
+
(Expo 57, 2026-10-01) `EverfurClinicRequestBatch` was about 1112 pt of unscrolled content on an 874 pt screen: once
|
|
136
|
+
a clinic was added, Send and Cancel were out of reach, there was no close control at the top, the Search control
|
|
137
|
+
looked like a second text field, and the keyboard could not be dismissed. It now has a header with the composer's
|
|
138
|
+
own `Cancel`, the form in a keyboard-avoiding ScrollView that dismisses the keyboard on a drag or a tap outside a
|
|
139
|
+
control, and `Send` pinned in a footer above the home indicator. The search is one field whose return key
|
|
140
|
+
searches; Search is an outline pill. No new strings. (#143)
|
|
141
|
+
- **React Native records draw their icons (React Native).** The records pictograms rendered as empty tinted discs
|
|
142
|
+
(the empty states, the upload screen) and squares (the dashboard's action rows). They now draw the consumer app's
|
|
143
|
+
Feather icons with Views, as the chat glyphs do: `file-text`, `list`, `inbox` and `refresh-cw` on the action rows;
|
|
144
|
+
`search`, `inbox`, `activity`, `clock`, `tool` and `wifi-off` per empty state; `upload-cloud` on the upload screen.
|
|
145
|
+
They are drawn in the brand ink and hidden from assistive technology. (#149)
|
|
146
|
+
- **The records footnote sentence shows once per screen (web and React Native).** On `Visits & records` inside
|
|
147
|
+
`EverfurRecordsPage` the production integration test of 0.7.1 saw "Everfur summarizes what your vet recorded.
|
|
148
|
+
It is informational, not diagnostic." three times: the server's disclaimer band (the same words), the record
|
|
149
|
+
screen's footnote and the page's footnote. A screen the page shows now draws no footnote of its own under the
|
|
150
|
+
page's, and a server disclaimer that says exactly what the footnote says is not drawn again. A server
|
|
151
|
+
disclaimer with any other words is still rendered verbatim. (#142)
|
|
152
|
+
- **The records consent gate says what `Allow` agrees to, and its button is reachable on React Native.** Both
|
|
153
|
+
production integration tests of 0.7.1 saw only "Consent required" and `Allow`. The gate now shows a line for
|
|
154
|
+
what the member consents to between them. **That line is a visible `DRAFT COPY [[...]]` placeholder**: no
|
|
155
|
+
approved consent text exists and the server serves none, so the wording is the owner's decision (documented in
|
|
156
|
+
09-GUIDE-RECORDS-AND-CONSENT.md). On React Native the gate's container was `accessible`, which grouped it into
|
|
157
|
+
one element named "Consent required" and hid the `Allow` button from a screen reader; the ask is now visible
|
|
158
|
+
text announced on mount, and `Allow` is its own button. (#150)
|
|
159
|
+
- **Web records forms and screens speak to assistive technology (accessibility).** A records flow opened on a given
|
|
160
|
+
screen (`initialScreen`) now moves focus to that screen's heading on mount, as every later screen change already
|
|
161
|
+
did; before, the host control that opened it unmounted and focus fell to the page's `<body>`. The clinic email
|
|
162
|
+
errors on the consent screen, a failed request's recovery and the clinic search's add-by-email form are bound to
|
|
163
|
+
their fields (`aria-invalid` + `aria-describedby`). The clinic search says how many clinics it found through a
|
|
164
|
+
polite status region (a DRAFT COPY slot until the owner approves a sentence). The upload card's heading is an
|
|
165
|
+
`h3` under the screen's `h2` instead of skipping to `h4`. (#161)
|
|
166
|
+
- **The records share sheet's audience chips wrap (web).** At 390px the `Boarding` chip was clipped on the right. (#145)
|
|
167
|
+
- **Every records control says what it shows (web and React Native, WCAG 2.5.3 Label in Name).** Owner decision
|
|
168
|
+
2026-10-02: a control's accessible name is its visible text, so a speech user can activate it by reading it. The
|
|
169
|
+
records screens and the chat history named about twenty controls in other words (`Choose a file` was spoken
|
|
170
|
+
"Choose records to upload", `I already have the records` "Upload records you already have", `Cancel request`
|
|
171
|
+
"Cancel this records request"); each now takes its visible text as its name, busy labels included. A control
|
|
172
|
+
repeated in a list keeps its visible name and gets its row as context: `Copy link` is described by its share row
|
|
173
|
+
(`aria-describedby` on the web, `accessibilityHint` on React Native) and each request row's `Ask about this
|
|
174
|
+
request` has the row's source as its hint. The clinic picker's add-by-email toggle states open or closed through
|
|
175
|
+
`aria-expanded` and `accessibilityState.expanded` instead of a second name. No visible copy changed. (#169)
|
|
176
|
+
- **The single-clinic consent form lets the keyboard go and keeps `Send request` above it (React Native).** Its
|
|
177
|
+
ScrollView and the clinic results dismiss the keyboard on a drag (interactive on iOS, on-drag on Android) and keep
|
|
178
|
+
taps on controls. The pinned `Send request` footer lifts by the keyboard measured from the avoiding view's own
|
|
179
|
+
top, as the multi-clinic composer's does (#143); before, it sat half under the keyboard. (#170)
|
|
180
|
+
- **The search glyph has its handle (React Native).** The clinic empty state's and the chat history's search glyph
|
|
181
|
+
drew its handle detached from the ring, and the clinic picker's search bar drew a bare ring. Both now draw
|
|
182
|
+
Feather's `search`: a ring with the handle growing out of it, down and to the right. (#170)
|
|
183
|
+
|
|
184
|
+
#### Chat
|
|
185
|
+
|
|
186
|
+
- **Chat shows the pet a host registers on load, without waiting for a send (web and React Native).** The pet
|
|
187
|
+
list behind the chat header, the empty-state title and the starter chips was read once on mount, so a host
|
|
188
|
+
that calls `registerPet` as the page loads (the documented shape) raced it: the production integration test of
|
|
189
|
+
0.7.1 opened chat before the write landed and saw "What can we help your pet with today?", "Mixed Breed" and a
|
|
190
|
+
`?` avatar until the member sent a message. A successful `registerPet`, `updatePet` or `deletePet` now makes
|
|
191
|
+
`useEverfurPets` (and so chat) read the list again. (#141)
|
|
192
|
+
- **Chat conversations are saved under the chat's pet again (web and React Native).** Since 0.7.0 the first
|
|
193
|
+
send creates the conversation (#129), and it created it with the turn's pet, which `useEverfurChat().send`
|
|
194
|
+
never names: every conversation was created unscoped and saved with `pet_ref` null, so the pet's history
|
|
195
|
+
drawer read "No chats yet" right after a chat (mobile integration test of 0.7.1). A send that names no pet now
|
|
196
|
+
uses the chat's own pet scope; a send that names one keeps it, and an unscoped chat is unchanged. (#148)
|
|
197
|
+
- **React Native chat lets the keyboard go, and the urgency banner compacts while typing (React Native).** Once
|
|
198
|
+
open, the chat keyboard could not be dismissed, and the pinned urgency banner kept its full height while the
|
|
199
|
+
member typed (about 40% of what was left of the screen). The thread and the starter-prompt ScrollViews now
|
|
200
|
+
dismiss the keyboard on a drag (interactive on iOS, on-drag on Android). While the keyboard is open the banner is
|
|
201
|
+
compact: tighter padding and the reason held to two lines (a screen reader still reads it whole). The tier label,
|
|
202
|
+
the vet pill, poison control and the dismiss stay, so an emergency keeps its way to care. (#147)
|
|
203
|
+
- **The web chat holds up in forced-colors mode (Windows High Contrast) (accessibility).** The header's New chat and
|
|
204
|
+
History glyphs vanished, the starter chips and the Send disc lost their edges, and the brand-ink upload and mic
|
|
205
|
+
glyphs fell below contrast. Every chat glyph now draws in `currentColor` with its ink as the SVG's CSS `color`
|
|
206
|
+
(and `forced-color-adjust: auto`, since browsers otherwise preserve an SVG's own colour there), so the mode
|
|
207
|
+
repaints it in the system colours; the starter chips, the header's icon buttons, the composer's glyph controls
|
|
208
|
+
and the Send disc carry a 1px transparent edge that only the mode draws. The normal look is unchanged. (#159)
|
|
209
|
+
- **The starter chips follow a partner's palette and the dark palette (React Native).** Off Everfur's own light look
|
|
210
|
+
the four starter chips stayed Everfur's blue, green, amber and pink pastels, which glared on the night ground. They
|
|
211
|
+
now paint the theme's `highlight` under `onHighlight`. Everfur's own look keeps its pastels and black ink. (#170)
|
|
212
|
+
- **The starter chips follow a partner's palette and the dark palette (web).** The web chat's starter chips kept
|
|
213
|
+
Everfur's blue, green, amber and pink pastels under every partner preset and glared on the night ground. Off
|
|
214
|
+
Everfur's own light look they now paint the theme's `highlight` under `onHighlight` (4.5:1), the rule the React
|
|
215
|
+
Native chips follow since #170. Everfur's own look keeps its pastels and black ink. (#172)
|
|
216
|
+
|
|
217
|
+
#### Vet visits
|
|
218
|
+
|
|
219
|
+
- **The default vet visit can offer the pet's own clinic and your emergency-vet locator (web and React Native).**
|
|
220
|
+
`primaryVet` and `onFindEmergencyVet` reached the emergency warning only through `EverfurTelevet`; the in-app
|
|
221
|
+
visit Everfur provides by default (`EverfurTelevetHost`) rendered it without them, so the warning offered ASPCA
|
|
222
|
+
Animal Poison Control and no way to the member's clinic. The web host now passes both through, as it does
|
|
223
|
+
`onPickMedia`. The React Native host took no props at all; it now takes the same four (`onAddToCalendar`,
|
|
224
|
+
`onPickMedia`, `primaryVet`, `onFindEmergencyVet`), and `@everfur/sdk/televet` exports their types. (#157)
|
|
225
|
+
- **The vet booking asks each question once (web and React Native).** Every booking read kept its own state, so the
|
|
226
|
+
dialog asked its eight or nine questions again on every open, the state policy twice per open (the flow and its
|
|
227
|
+
`where` step), and ten open/close cycles ran a member into the partner plane's rate limit (2026-10-01 audit). The
|
|
228
|
+
read-only reads (the roster, the slots, the soonest opening, the state policy, the cancellation terms, the visits)
|
|
229
|
+
now share one answer per question per signed-in session: a question already in flight is joined, a settled answer
|
|
230
|
+
is reused for 30 seconds (the visits list a member comes back to is still read again), and a hold, booking,
|
|
231
|
+
move, cancel or released hold drops the answers it changed. The entitlement read is joined but never reused. A
|
|
232
|
+
rate-limited answer is kept for the server's own wait, so a reopened dialog shows the rate-limit state it already
|
|
233
|
+
has instead of spending another request. A 429 or 503 whose wait rides only in the `Retry-After` header is now
|
|
234
|
+
honoured like one whose body names it. (#166)
|
|
235
|
+
- **A test tenant's vet entry no longer reads the real vet roster or slots.** The booking container prefetched the
|
|
236
|
+
real roster, bios, photos and bookable slots even when it then drew the visit simulator. On a test tenant those
|
|
237
|
+
reads now wait for the allowance answer and never start when the simulator applies. A live tenant reads them at
|
|
238
|
+
once, as before; a test tenant with an allowance still books real visits. (#162)
|
|
239
|
+
- **A rate-limited booking read says so, and offers "Try again" when it can work (web and React Native).** After
|
|
240
|
+
about eight booking opens in eight minutes the production integration test of 0.7.1 got 429 on the vet roster
|
|
241
|
+
and the state policy, and the step showed only "Could not load" with a "Try again" that could not succeed.
|
|
242
|
+
The pet, vet and time steps now show the SDK's existing rate-limit state ("Slow down a moment" / "You’ve made
|
|
243
|
+
a lot of requests just now. You can try again shortly.", the records empty state's words) and offer "Try
|
|
244
|
+
again" once the server's `retry_after_seconds` has passed (60 seconds when it names none). Any other failure
|
|
245
|
+
shows the step's own card, as before. The steps take an optional `error` prop for this. (#146)
|
|
246
|
+
- **An unbooked vet visit hold is given back (web and React Native).** Reaching the booking review takes a
|
|
247
|
+
ten-minute hold, and nothing released it: closing the dialog left the time off every grid until the hold ran
|
|
248
|
+
out, and the same member coming back for it read "That time was just taken." about their own hold (production
|
|
249
|
+
integration test of 0.7.1). The booking flow now releases its hold when it closes or unmounts without booking,
|
|
250
|
+
and when another time replaces it. Back from review still keeps the hold, so the same time picked again goes
|
|
251
|
+
straight back to review on it. A hold the flow booked, or is booking, is never released. (#144)
|
|
252
|
+
- **No fee line on a partner's cancel receipt (web).** A partner member owes no cancellation fee (owner decision
|
|
253
|
+
2026-10-01: a late cancel is used from the partner's allowance, and the server reports `fee_recorded: false` on
|
|
254
|
+
every partner cancel), yet the visits screen showed "No late cancellation fee was recorded for this visit." on
|
|
255
|
+
every receipt. That line now renders only when the server reports a recorded fee, so a fee that was recorded is
|
|
256
|
+
still named. No copy changed. (#153)
|
|
257
|
+
- **React Native booking follows the palette.** The progress bar (the booking chrome's done segments) and the
|
|
258
|
+
booked and joined rings now take `mark` instead of Everfur's green, the picked day `highlight` instead of the pink,
|
|
259
|
+
the primary button's edge `primaryEdge` (a pale partner primary, the disabled `Continue` included, keeps its
|
|
260
|
+
shape), and the full-screen booking modal sets the status bar to light content over a dark ground (the `night`
|
|
261
|
+
preset left the iOS clock dark on dark). (#145)
|
|
262
|
+
- **The booking emergency panel is as tall as its content (web and React Native).** The early emergency step grew
|
|
263
|
+
to the dialog's height, a tall red box with nothing in its lower half. (#145)
|
|
264
|
+
- **The vet visit dialog states `aria-modal` (web).** It is a `<dialog>` shown with `showModal()`, so the browser
|
|
265
|
+
already treats it as modal and the page behind it as inert; it now says so in the attribute too, as the records
|
|
266
|
+
dialogs (the share sheet's among them) do, for tools that read attributes and for a DOM without `showModal`. (#164)
|
|
267
|
+
- **The web booking's choices and times are walked with the arrow keys (accessibility).** The option cards (pet,
|
|
268
|
+
where, the emergency check, a cancellation's reason) and the time grid made every choice its own tab stop. Each
|
|
269
|
+
radio group is now one tab stop whose arrows move the pick and wrap, as native radios do; the time grid is one tab
|
|
270
|
+
stop walked with Left, Right, Up, Down, Home and End, where focus never holds a slot (Enter or a click does), and
|
|
271
|
+
a chip stays focusable while its hold is in flight. The review summary is a valid description list again (its
|
|
272
|
+
"Change" sits inside the value it changes), and the visit dialog is named by the heading on screen rather than
|
|
273
|
+
by a title no screen shows. (#156)
|
|
274
|
+
- **The web booking's emergency warning comes into view, and a busy button keeps the focus (accessibility).** At
|
|
275
|
+
200% zoom (640x450) "Yes, at least one" opened the emergency warning below the step's scroll area: it was
|
|
276
|
+
announced, but a sighted member saw only a Continue that had gone dark. The warning is now scrolled into view
|
|
277
|
+
(`block: 'nearest'`, instant under reduced motion), and the React Native step scrolls to it the same way. A booking
|
|
278
|
+
button that went busy or blocked used the native `disabled` attribute, which drops focus to the partner page's
|
|
279
|
+
`<body>` mid-booking; it is now `aria-disabled`, stays focusable, swallows the press, points `aria-describedby` at
|
|
280
|
+
the reason where the screen shows one, and speaks the deck's own busy words ("Booking…", "Moving…") through a
|
|
281
|
+
polite status region. The step's scroller now contains the review's visually hidden consent inputs, which had
|
|
282
|
+
stretched the visit dialog's own scroll height and scrolled the whole dialog when one took focus. (#154)
|
|
283
|
+
|
|
284
|
+
### Documentation
|
|
285
|
+
|
|
286
|
+
- **The docs say what the UI and the wire do (production sandbox and Next.js integration tests, 2026-10-01).**
|
|
287
|
+
The vet booking is eight steps with a pet chooser and seven for a member with one pet, not always eight. The chat
|
|
288
|
+
stream's `metadata` frame arrives BEFORE `done`, which repeats its title and follow-up questions; 10-TESTING
|
|
289
|
+
said a frame arrives after `done` and the quickstart said a later `metadata` frame can follow. The examples
|
|
290
|
+
script no longer says a sandbox uses an ordinary `pk_live_` key on staging, and the onboarding page no longer says
|
|
291
|
+
most sandboxes come from self-serve sign-up, which is not open to partners yet. (#165)
|
|
292
|
+
- **Current facts in the README and the vet visit guide.** The README named `0.3.0` and called vet visits "not
|
|
293
|
+
yet enabled", and 16-TELEVET-VISITS.md opened with "draft ... not yet enabled for any partner"; vet visits run
|
|
294
|
+
in production for the partners Everfur enables them for. The README quickstart's `getToken` used a relative
|
|
295
|
+
URL, which does not resolve in a React Native bundle (00-QUICKSTART.md already said so). The camera note said
|
|
296
|
+
to add vision-camera's config plugin: `4.x` has one, `5.x` has none and needs `react-native-nitro-modules` and
|
|
297
|
+
`react-native-nitro-image`. The React Native call setup now names all of Daily's declared peers
|
|
298
|
+
(`@react-native-async-storage/async-storage`, `react-native-background-timer`, `react-native-get-random-values`
|
|
299
|
+
beside the WebRTC build) and the config plugin's one-Expo-SDK peer, with the `--legacy-peer-deps` install and
|
|
300
|
+
the `overrides` entry that keeps the next install and `npm ci` passing. `registerEverfurDaily`'s doc comment on
|
|
301
|
+
React Native now imports it from `@everfur/sdk/televet/call`, where it is exported. Found by the 2026-10-01
|
|
302
|
+
production integration tests of 0.7.1 (web and mobile). (#151)
|
|
303
|
+
- **Test mode is a production sandbox with a `pk_test_` key.** 00-QUICKSTART, 10-TESTING, 12-CLI-AND-MCP and the
|
|
304
|
+
README describe test mode as a sandbox tenant with a `pk_test_` key on the production host (an older sandbox may
|
|
305
|
+
still carry `pk_live_`; staging is early access), the go-live pair, the console's live/test switch, the hosted MCP
|
|
306
|
+
reconnect and the test/staging/production plan environments; 02, 08, 11 and 14 no longer say that no deployment
|
|
307
|
+
accepts `pk_test_`. The operator guide (04-ONBOARDING-A-CUSTOMER) covers going live by pairing. Every heading and
|
|
308
|
+
anchor is unchanged. (#101)
|
|
309
|
+
|
|
310
|
+
### Tests and tooling
|
|
311
|
+
|
|
312
|
+
- **The records bundle budget is re-measured.** The 2026-10-01 records fixes (#139, #142, #143, #149) each passed
|
|
313
|
+
on their own head and together crossed the React Native records closure's line by 349 gzip bytes, all records
|
|
314
|
+
feature code; the closure still holds no chat, vet visit, video, photo or animations module. The line is the CI
|
|
315
|
+
measurement plus 5%, as every budget entry is. (#152)
|
|
316
|
+
- **Chat's first reads are pinned beside the entitlement verdict.** A test holds the verdict unanswered over the
|
|
317
|
+
real transport and asserts the conversation head, the suggested prompts and the pets requests are already out, so
|
|
318
|
+
a later gate on the verdict cannot add that hop. (#167)
|
|
319
|
+
- **The consent gate's `Allow` is pinned reachable on the dashboard, and the theme guard's development warning is
|
|
320
|
+
pinned to a runtime switch of appearance.** No ancestor of `Allow` on the React Native dashboard is `accessible`;
|
|
321
|
+
switching to another adjusted preset at runtime writes that preset's line once, and a look the guard leaves alone
|
|
322
|
+
writes nothing. (#170)
|
|
323
|
+
|
|
13
324
|
## [0.7.1] - 2026-10-01
|
|
14
325
|
|
|
15
326
|
### Fixed
|
package/README.md
CHANGED
|
@@ -10,12 +10,15 @@ features without shipping an app update.
|
|
|
10
10
|
a blank screen, and cannot be force-enabled client-side.
|
|
11
11
|
- The app never holds a secret. See [The security model](#the-security-model).
|
|
12
12
|
|
|
13
|
-
> `0.
|
|
13
|
+
> `0.8.0`, MIT licensed, public on npm. Requires React Native `>=0.74` (or React `>=18` with `react-dom`
|
|
14
14
|
> for the web entry), and Node `>=18` on the backend that mints sessions.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
<!-- DRAFT COPY slot PD1-2 (owner): the README's invitation to get started (self-serve sandbox sign-up), in the owner's words. The factual paragraph below stands without it. -->
|
|
17
|
+
**Getting keys:** a sandbox (test-mode) tenant can be created from the partner console's sandbox page
|
|
18
|
+
(`https://everfur.com/partners/console/sandbox`) where Everfur has opened sandbox sign-up; otherwise the
|
|
19
|
+
Everfur team creates it. Live tenants and their keys are provisioned by the Everfur team. Contact
|
|
20
|
+
[support@everfur.com](mailto:support@everfur.com) to get set up. The publishable key is designed to ship
|
|
21
|
+
inside your app; the secret key is not.
|
|
19
22
|
|
|
20
23
|
## Install
|
|
21
24
|
|
|
@@ -36,8 +39,9 @@ Peer dependencies by capability (install only what you use):
|
|
|
36
39
|
|
|
37
40
|
The camera peer is optional: if you never mount `./video` or `./photo`, you do not need it.
|
|
38
41
|
`EverfurRecords` accepts the host's picker and upload transport, so use the native modules that fit your
|
|
39
|
-
app (including Expo SDK 57) without an SDK peer-dependency constraint. `react-native-vision-camera`
|
|
40
|
-
|
|
42
|
+
app (including Expo SDK 57) without an SDK peer-dependency constraint. `react-native-vision-camera` needs a
|
|
43
|
+
camera usage description; `4.x` also ships an Expo config plugin (add it to `app.json` plugins), while `5.x`
|
|
44
|
+
ships none and instead needs its own peers `react-native-nitro-modules` and `react-native-nitro-image`.
|
|
41
45
|
|
|
42
46
|
### Bundler resolution
|
|
43
47
|
|
|
@@ -70,8 +74,8 @@ Two ways, one wire contract, documented in
|
|
|
70
74
|
is [`examples/frame-embed.html`](https://sdk.everfur.com/v1/docs/index.html#web--the-snippet).
|
|
71
75
|
|
|
72
76
|
Chat and records are the web surfaces (records on its own subpath and its own frame document, available
|
|
73
|
-
when Everfur enables records for your account); photo and video remain React Native for now. Vet visits (
|
|
74
|
-
have their own entry on both hosts, `@everfur/sdk/web/televet` and `@everfur/sdk/televet`, and a visit opens
|
|
77
|
+
when Everfur enables records for your account); photo and video remain React Native for now. Vet visits (available when Everfur enables them for your
|
|
78
|
+
account) have their own entry on both hosts, `@everfur/sdk/web/televet` and `@everfur/sdk/televet`, and a visit opens
|
|
75
79
|
INSIDE your app by default: booking, the confirmation and the call run in an Everfur modal (React Native) or
|
|
76
80
|
dialog (web) that `VetVisitButton` and `EverfurChat` mount themselves; the hosted page is an explicit opt-in,
|
|
77
81
|
`prefer="redirect"`. The booking screens (`@everfur/sdk/televet/booking`, `@everfur/sdk/web/televet/booking`)
|
|
@@ -93,13 +97,14 @@ export default function App() {
|
|
|
93
97
|
return (
|
|
94
98
|
<EverfurProvider
|
|
95
99
|
config={{
|
|
96
|
-
publishableKey: process.env.EXPO_PUBLIC_EVERFUR_PUBLISHABLE_KEY!, // pk_live_... safe to ship
|
|
100
|
+
publishableKey: process.env.EXPO_PUBLIC_EVERFUR_PUBLISHABLE_KEY!, // pk_test_... or pk_live_... safe to ship
|
|
97
101
|
apiBaseUrl: 'https://api.everfur.com/api/v1',
|
|
98
102
|
}}
|
|
99
103
|
// Omit `user` entirely for anonymous mode (publishable key alone, general answers).
|
|
100
104
|
user={{
|
|
101
105
|
userRef: userRef('your-stable-user-id'),
|
|
102
|
-
|
|
106
|
+
// An absolute URL: a relative path does not resolve in a React Native bundle.
|
|
107
|
+
getToken: () => fetch('https://your-backend.example.com/everfur/session', { method: 'POST' }).then((r) => r.text()),
|
|
103
108
|
}}
|
|
104
109
|
activePet={petRef('pet-ref-123')}
|
|
105
110
|
>
|
|
@@ -140,23 +145,25 @@ import { petRef } from '@everfur/sdk';
|
|
|
140
145
|
## The security model
|
|
141
146
|
|
|
142
147
|
The one rule: **the app never holds a secret.** Two provisioning credentials, kept apart, plus a read-only
|
|
143
|
-
docs key for tooling. Test mode is a sandbox tenant
|
|
144
|
-
(`livemode: false` in its entitlements);
|
|
148
|
+
docs key for tooling. Test mode is a sandbox tenant with a `pk_test_` key on the production API
|
|
149
|
+
(`livemode: false` in its entitlements); the API accepts a `pk_test_` key only for a sandbox tenant, so a test
|
|
150
|
+
key never reaches live data. An older sandbox may still carry a `pk_live_` key, which keeps working, so read
|
|
151
|
+
`livemode`, not the prefix. Staging (`https://api-staging.everfur.com/api/v1`) is for early access.
|
|
145
152
|
|
|
146
153
|
| Credential | Where it lives | What it does |
|
|
147
154
|
| --- | --- | --- |
|
|
148
|
-
| Publishable key `pk_live_...` | In the app (`EXPO_PUBLIC_...`). Safe to ship. | Names your tenant. Cannot mint a session or grant a feature by itself. |
|
|
155
|
+
| Publishable key `pk_test_...` (sandbox) or `pk_live_...` (live) | In the app (`EXPO_PUBLIC_...`). Safe to ship. | Names your tenant. Cannot mint a session or grant a feature by itself. |
|
|
149
156
|
| Partner secret key `sk_partner_...` | **Only** on your server. Never in the app, its bundle, env, git, or logs. | Mints session tokens. Revocable and rotatable without an app release. |
|
|
150
157
|
| Session token (bearer) | App memory, short-lived. | Authenticates one end user; re-minted via your backend on a 401. Signed, holds no secret, 15 minutes by default (1 minute to 1 hour). |
|
|
151
158
|
| Docs key `dk_partner_...` | Only in the MCP server or CLI configuration on a developer machine. | Read-only: fetches the Everfur contract from `GET /partners/contract` and opens no other route. |
|
|
152
159
|
|
|
153
160
|
Your backend exchanges the two provisioning credentials for a short-lived session by calling
|
|
154
|
-
`POST /widget/v1/sessions` with `x-everfur-partner-key:
|
|
155
|
-
sk_partner_
|
|
161
|
+
`POST /widget/v1/sessions` with `x-everfur-partner-key: pk_...` + `x-everfur-partner-secret-key:
|
|
162
|
+
sk_partner_...`, both from the same tenant. The SDK ships a **server-only** helper (`mintPartnerSession`, imported from
|
|
156
163
|
`@everfur/sdk/server`) so you do not hand-roll that request. That helper is deliberately absent from every
|
|
157
164
|
React-Native-resolvable bundle: a device can never import it, so the secret has no path onto the phone.
|
|
158
165
|
|
|
159
|
-
**Do:** ship `pk_live_` in the app; keep `sk_partner_` in a server secret manager; mint sessions on your
|
|
166
|
+
**Do:** ship the publishable key (`pk_test_` or `pk_live_`) in the app; keep `sk_partner_` in a server secret manager; mint sessions on your
|
|
160
167
|
backend; serve everything over HTTPS.
|
|
161
168
|
**Never:** put `sk_partner_` in the app; mint sessions on the device; try to force-enable a feature
|
|
162
169
|
client-side (the server re-checks and returns `403`).
|
|
@@ -185,6 +192,7 @@ off-state, never a blank region.
|
|
|
185
192
|
| `@everfur/sdk/animations` | server-driven pet-state media surface |
|
|
186
193
|
| `@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
194
|
| `@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)) |
|
|
195
|
+
| `@everfur/sdk/web/records/upload` | `createWebRecordsUploadTransport` alone, for the `uploadTransport` on the provider config at your app root; the same function `@everfur/sdk/web/records` exports, without the records entry |
|
|
188
196
|
| `@everfur/sdk/server` | **server-only** `mintPartnerSession` (no React Native resolution; never bundled on-device) |
|
|
189
197
|
| `@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
198
|
| `@everfur/sdk/testing`, `@everfur/sdk/testing/rn`, `@everfur/sdk/testing/web` | test seams (`MockTransport`, `useEverfurTestEntitlements`, ...) for your own tests |
|
|
@@ -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-
|
|
6
|
-
import { W as WidgetPet, P as PetProfileInput } from './petsRepository-
|
|
7
|
-
import { U as UrgencyDisplayLevel, L as LifecyclePort, E as EverfurChatDraftStore } from './attachments-
|
|
5
|
+
import { a as ChatCheckinTarget } from './ports-BUXF0Rp3.js';
|
|
6
|
+
import { W as WidgetPet, P as PetProfileInput } from './petsRepository-B2lO1sKS.js';
|
|
7
|
+
import { U as UrgencyDisplayLevel, L as LifecyclePort, E as EverfurChatDraftStore } from './attachments-y_aO4hlk.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-
|
|
6
|
-
import { W as WidgetPet, P as PetProfileInput } from './petsRepository-
|
|
7
|
-
import { U as UrgencyDisplayLevel, L as LifecyclePort, E as EverfurChatDraftStore } from './attachments-
|
|
5
|
+
import { a as ChatCheckinTarget } from './ports-C40S2V14.cjs';
|
|
6
|
+
import { W as WidgetPet, P as PetProfileInput } from './petsRepository-BP9nhM1R.cjs';
|
|
7
|
+
import { U as UrgencyDisplayLevel, L as LifecyclePort, E as EverfurChatDraftStore } from './attachments-DbFwz4iq.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-
|
|
2
|
+
import { f as RecordsScreenBaseProps } from './useRecordsDepth-DuPUJFDQ.js';
|
|
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-
|
|
3
|
+
import { f as RecordsScreenBaseProps } from './useRecordsDepth-D6a6-_u_.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-
|
|
3
|
+
import { f as RecordsScreenBaseProps } from './useRecordsDepth-DuPUJFDQ.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-
|
|
2
|
+
import { f as RecordsScreenBaseProps } from './useRecordsDepth-D6a6-_u_.cjs';
|
|
3
3
|
|
|
4
4
|
interface EverfurRecordsDepthProps extends Pick<RecordsScreenBaseProps, 'petRef' | 'petName' | 'onNavigate' | 'onBack'> {
|
|
5
5
|
readonly style?: CSSProperties;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as EverfurErrorCode, E as EverfurError } from './EverfurError-
|
|
1
|
+
import { b as EverfurErrorCode, E as EverfurError } from './EverfurError-pEXDbUJ1.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Recovery policy. A host may narrow retry ceilings (e.g. a metered plan) but can never widen the SDK's
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as EverfurErrorCode, E as EverfurError } from './EverfurError-
|
|
1
|
+
import { b as EverfurErrorCode, E as EverfurError } from './EverfurError-pEXDbUJ1.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Recovery policy. A host may narrow retry ceilings (e.g. a metered plan) but can never widen the SDK's
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The frozen error taxonomy. Additive-only under the dated-contract policy. */
|
|
2
|
-
type EverfurErrorCode = 'authExpired' | 'authRejected' | 'sessionRequired' | 'accessDenied' | 'paymentRequired' | 'quotaExceeded' | 'rateLimited' | 'validationFailed' | 'sdkUpdateRequired' | 'conflict' | 'idempotencyConflict' | 'idempotencyInFlight' | 'conversationNotFound' | 'petNotFound' | 'resourceNotFound' | 'streamTimeout' | 'mediaUploadFailed' | 'moderationBlocked' | 'upstreamLlmTimeout' | 'internalError' | 'notImplemented' | 'serviceUnavailable' | 'streamIncomplete' | 'unknown';
|
|
2
|
+
type EverfurErrorCode = 'authExpired' | 'authRejected' | 'sessionRequired' | 'accessDenied' | 'paymentRequired' | 'quotaExceeded' | 'rateLimited' | 'validationFailed' | 'sdkUpdateRequired' | 'conflict' | 'idempotencyConflict' | 'idempotencyInFlight' | 'conversationNotFound' | 'petNotFound' | 'resourceNotFound' | 'streamTimeout' | 'mediaUploadFailed' | 'moderationBlocked' | 'upstreamLlmTimeout' | 'internalError' | 'notImplemented' | 'serviceUnavailable' | 'streamIncomplete' | 'identitySignatureRequired' | 'unknown';
|
|
3
3
|
/** Every code, frozen, for iteration in conformance / property tests. */
|
|
4
4
|
declare const EVERFUR_ERROR_CODES: readonly EverfurErrorCode[];
|
|
5
5
|
/**
|
|
@@ -137,6 +137,12 @@ interface EverfurError {
|
|
|
137
137
|
* snake_case token is kept; anything else is dropped rather than carried.
|
|
138
138
|
*/
|
|
139
139
|
readonly reasonCode?: string;
|
|
140
|
+
/**
|
|
141
|
+
* The wait the server named, in seconds, when it named one: the body's `retry_after_seconds`, or a `Retry-After`
|
|
142
|
+
* header on a 429 or 503. The anonymous-session budget's 429 (`reasonCode` `anonymous_session_limit`) carries
|
|
143
|
+
* it; the SDK does not retry past it.
|
|
144
|
+
*/
|
|
145
|
+
readonly retryAfterSeconds?: number;
|
|
140
146
|
/** raw wire text; telemetry / logs only, NEVER rendered. NON-ENUMERABLE. */
|
|
141
147
|
readonly wireMessage?: string;
|
|
142
148
|
/** Opaque parsed wire payload for debugging (untrusted, unvalidated). NON-ENUMERABLE. */
|
|
@@ -169,6 +175,8 @@ interface MakeEverfurErrorOptions {
|
|
|
169
175
|
readonly reasonCode?: string | null;
|
|
170
176
|
/** Opaque parsed wire payload. Carried NON-ENUMERABLY for debugging; never rendered. */
|
|
171
177
|
readonly raw?: unknown;
|
|
178
|
+
/** The wait the response named, in seconds. Kept only when finite and not negative. */
|
|
179
|
+
readonly retryAfterSeconds?: number | null;
|
|
172
180
|
}
|
|
173
181
|
declare function makeEverfurError(code: EverfurErrorCode, opts?: MakeEverfurErrorOptions): EverfurError;
|
|
174
182
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The frozen error taxonomy. Additive-only under the dated-contract policy. */
|
|
2
|
-
type EverfurErrorCode = 'authExpired' | 'authRejected' | 'sessionRequired' | 'accessDenied' | 'paymentRequired' | 'quotaExceeded' | 'rateLimited' | 'validationFailed' | 'sdkUpdateRequired' | 'conflict' | 'idempotencyConflict' | 'idempotencyInFlight' | 'conversationNotFound' | 'petNotFound' | 'resourceNotFound' | 'streamTimeout' | 'mediaUploadFailed' | 'moderationBlocked' | 'upstreamLlmTimeout' | 'internalError' | 'notImplemented' | 'serviceUnavailable' | 'streamIncomplete' | 'unknown';
|
|
2
|
+
type EverfurErrorCode = 'authExpired' | 'authRejected' | 'sessionRequired' | 'accessDenied' | 'paymentRequired' | 'quotaExceeded' | 'rateLimited' | 'validationFailed' | 'sdkUpdateRequired' | 'conflict' | 'idempotencyConflict' | 'idempotencyInFlight' | 'conversationNotFound' | 'petNotFound' | 'resourceNotFound' | 'streamTimeout' | 'mediaUploadFailed' | 'moderationBlocked' | 'upstreamLlmTimeout' | 'internalError' | 'notImplemented' | 'serviceUnavailable' | 'streamIncomplete' | 'identitySignatureRequired' | 'unknown';
|
|
3
3
|
/** Every code, frozen, for iteration in conformance / property tests. */
|
|
4
4
|
declare const EVERFUR_ERROR_CODES: readonly EverfurErrorCode[];
|
|
5
5
|
/**
|
|
@@ -137,6 +137,12 @@ interface EverfurError {
|
|
|
137
137
|
* snake_case token is kept; anything else is dropped rather than carried.
|
|
138
138
|
*/
|
|
139
139
|
readonly reasonCode?: string;
|
|
140
|
+
/**
|
|
141
|
+
* The wait the server named, in seconds, when it named one: the body's `retry_after_seconds`, or a `Retry-After`
|
|
142
|
+
* header on a 429 or 503. The anonymous-session budget's 429 (`reasonCode` `anonymous_session_limit`) carries
|
|
143
|
+
* it; the SDK does not retry past it.
|
|
144
|
+
*/
|
|
145
|
+
readonly retryAfterSeconds?: number;
|
|
140
146
|
/** raw wire text; telemetry / logs only, NEVER rendered. NON-ENUMERABLE. */
|
|
141
147
|
readonly wireMessage?: string;
|
|
142
148
|
/** Opaque parsed wire payload for debugging (untrusted, unvalidated). NON-ENUMERABLE. */
|
|
@@ -169,6 +175,8 @@ interface MakeEverfurErrorOptions {
|
|
|
169
175
|
readonly reasonCode?: string | null;
|
|
170
176
|
/** Opaque parsed wire payload. Carried NON-ENUMERABLY for debugging; never rendered. */
|
|
171
177
|
readonly raw?: unknown;
|
|
178
|
+
/** The wait the response named, in seconds. Kept only when finite and not negative. */
|
|
179
|
+
readonly retryAfterSeconds?: number | null;
|
|
172
180
|
}
|
|
173
181
|
declare function makeEverfurError(code: EverfurErrorCode, opts?: MakeEverfurErrorOptions): EverfurError;
|
|
174
182
|
|