@everfur/sdk 0.1.1 → 0.4.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.
Files changed (219) hide show
  1. package/CHANGELOG.md +703 -4
  2. package/README.md +63 -23
  3. package/consent/package.json +8 -0
  4. package/dist/Chat-BZZ-Py5l.d.ts +128 -0
  5. package/dist/Chat-Dq4fffIO.d.cts +128 -0
  6. package/dist/DepthViews-BG3E25MD.d.ts +16 -0
  7. package/dist/DepthViews-B_hIypI6.d.cts +19 -0
  8. package/dist/DepthViews-CL75jyRA.d.cts +16 -0
  9. package/dist/DepthViews-CkYri51q.d.ts +19 -0
  10. package/dist/ErrorPolicyPort-CNf4uQZP.d.cts +22 -0
  11. package/dist/ErrorPolicyPort-CwbfmxzJ.d.ts +22 -0
  12. package/dist/{EverfurResult-D92-uL82.d.cts → EverfurResult-DN9pL2Ab.d.cts} +11 -10
  13. package/dist/{EverfurResult-D92-uL82.d.ts → EverfurResult-DN9pL2Ab.d.ts} +11 -10
  14. package/dist/{PhotoController-BItt5M7u.d.cts → PhotoController-3l7MT9jA.d.cts} +1 -1
  15. package/dist/{PhotoController-D8zMTdcW.d.ts → PhotoController-vzY5bfY_.d.ts} +1 -1
  16. package/dist/animations/index.cjs +1 -1997
  17. package/dist/animations/index.d.cts +35 -35
  18. package/dist/animations/index.d.ts +35 -35
  19. package/dist/animations/index.js +1 -1972
  20. package/dist/attachments-BorG422I.d.cts +146 -0
  21. package/dist/attachments-BrUWQYNK.d.ts +257 -0
  22. package/dist/attachments-D_SECjr1.d.cts +257 -0
  23. package/dist/attachments-DpldrvIW.d.ts +146 -0
  24. package/dist/bookingCopy-CqtDYvCR.d.ts +732 -0
  25. package/dist/bookingCopy-SREvkG19.d.cts +732 -0
  26. package/dist/branding-CsRdeNwK.d.cts +566 -0
  27. package/dist/branding-CsRdeNwK.d.ts +566 -0
  28. package/dist/callCopy-DoQKG3-L.d.cts +40 -0
  29. package/dist/callCopy-DoQKG3-L.d.ts +40 -0
  30. package/dist/casesRepository-DwtZVsbj.d.cts +51 -0
  31. package/dist/casesRepository-v6QczS9N.d.ts +51 -0
  32. package/dist/chat/index.cjs +1 -1170
  33. package/dist/chat/index.d.cts +32 -53
  34. package/dist/chat/index.d.ts +32 -53
  35. package/dist/chat/index.js +1 -1167
  36. package/dist/client/index.cjs +9 -2315
  37. package/dist/client/index.d.cts +80 -16
  38. package/dist/client/index.d.ts +80 -16
  39. package/dist/client/index.js +9 -2215
  40. package/dist/{config-CiJ0PVBB.d.ts → config--Wu1Umhd.d.cts} +64 -26
  41. package/dist/{config-BSjBdxrZ.d.cts → config-C7eb_IMD.d.ts} +64 -26
  42. package/dist/consent/index.cjs +1 -0
  43. package/dist/consent/index.d.cts +33 -0
  44. package/dist/consent/index.d.ts +33 -0
  45. package/dist/consent/index.js +1 -0
  46. package/dist/context-CL3lyuy1.d.cts +159 -0
  47. package/dist/context-CahAEha8.d.ts +159 -0
  48. package/dist/copy-DdVFDL01.d.ts +198 -0
  49. package/dist/copy-UeUJVk8X.d.cts +198 -0
  50. package/dist/core/index.cjs +13 -3178
  51. package/dist/core/index.d.cts +164 -29
  52. package/dist/core/index.d.ts +164 -29
  53. package/dist/core/index.js +13 -3166
  54. package/dist/depth-D5HwcvKC.d.cts +533 -0
  55. package/dist/depth-DvPwPuT_.d.ts +533 -0
  56. package/dist/entitlementRepository-B7egEdW6.d.ts +43 -0
  57. package/dist/entitlementRepository-BpJ7tiwl.d.cts +43 -0
  58. package/dist/{identity-Brl-lDd6.d.cts → identity-C2-08BZW.d.cts} +20 -7
  59. package/dist/{identity-DK9zORrG.d.ts → identity-CEi9GRFZ.d.ts} +20 -7
  60. package/dist/{ids-CJ1S6adf.d.cts → ids-B2GAAifq.d.cts} +1 -1
  61. package/dist/{ids-CJ1S6adf.d.ts → ids-B2GAAifq.d.ts} +1 -1
  62. package/dist/index.cjs +12 -4494
  63. package/dist/index.d.cts +30 -121
  64. package/dist/index.d.ts +30 -121
  65. package/dist/index.js +12 -4470
  66. package/dist/models-B-Uh2LTf.d.ts +214 -0
  67. package/dist/models-D2EUxPZY.d.cts +214 -0
  68. package/dist/notifications/index.cjs +2 -0
  69. package/dist/notifications/index.d.cts +17 -0
  70. package/dist/notifications/index.d.ts +17 -0
  71. package/dist/notifications/index.js +2 -0
  72. package/dist/optionalModule-DbJmsq5f.d.cts +21 -0
  73. package/dist/optionalModule-DbJmsq5f.d.ts +21 -0
  74. package/dist/pets-DJv2sj2H.d.ts +92 -0
  75. package/dist/pets-JVCXgRgR.d.cts +92 -0
  76. package/dist/petsRepository-B_gKbAzJ.d.cts +142 -0
  77. package/dist/petsRepository-Bk6GDwLs.d.ts +142 -0
  78. package/dist/photo/index.cjs +1 -2189
  79. package/dist/photo/index.d.cts +6 -6
  80. package/dist/photo/index.d.ts +6 -6
  81. package/dist/photo/index.js +1 -2186
  82. package/dist/ports-BN6RHF9W.d.cts +48 -0
  83. package/dist/ports-BN6RHF9W.d.ts +48 -0
  84. package/dist/ports-BY2ph0_y.d.cts +376 -0
  85. package/dist/ports-D7YW8qz9.d.ts +376 -0
  86. package/dist/profile-CSs1wlXT.d.cts +93 -0
  87. package/dist/profile-CSs1wlXT.d.ts +93 -0
  88. package/dist/records/depth/index.cjs +2 -0
  89. package/dist/records/depth/index.d.cts +13 -0
  90. package/dist/records/depth/index.d.ts +13 -0
  91. package/dist/records/depth/index.js +2 -0
  92. package/dist/records/index.cjs +3 -2839
  93. package/dist/records/index.d.cts +137 -203
  94. package/dist/records/index.d.ts +137 -203
  95. package/dist/records/index.js +3 -2833
  96. package/dist/requestFunnel-BjDpqqvm.d.cts +110 -0
  97. package/dist/requestFunnel-DFMGcluw.d.ts +110 -0
  98. package/dist/{resolve-Dq_4_agU.d.ts → resolve-D4Ywz5OS.d.cts} +10 -4
  99. package/dist/{resolve-Dq_4_agU.d.cts → resolve-D4Ywz5OS.d.ts} +10 -4
  100. package/dist/{runtime-Bvr8yqXm.d.cts → runtimeTypes-gZEJu-da.d.cts} +104 -132
  101. package/dist/{runtime-BIYzf9cK.d.ts → runtimeTypes-w5KqBMcs.d.ts} +104 -132
  102. package/dist/server/events/index.cjs +2 -0
  103. package/dist/server/events/index.d.cts +550 -0
  104. package/dist/server/events/index.d.ts +550 -0
  105. package/dist/server/events/index.js +2 -0
  106. package/dist/server/index.cjs +2 -533
  107. package/dist/server/index.d.cts +73 -18
  108. package/dist/server/index.d.ts +73 -18
  109. package/dist/server/index.js +2 -530
  110. package/dist/species-BXAIMh7I.d.cts +9 -0
  111. package/dist/species-BXAIMh7I.d.ts +9 -0
  112. package/dist/televet/booking/index.cjs +1 -0
  113. package/dist/televet/booking/index.d.cts +294 -0
  114. package/dist/televet/booking/index.d.ts +294 -0
  115. package/dist/televet/booking/index.js +1 -0
  116. package/dist/televet/call/index.cjs +1 -0
  117. package/dist/televet/call/index.d.cts +141 -0
  118. package/dist/televet/call/index.d.ts +141 -0
  119. package/dist/televet/call/index.js +1 -0
  120. package/dist/televet/index.cjs +1 -0
  121. package/dist/televet/index.d.cts +142 -0
  122. package/dist/televet/index.d.ts +142 -0
  123. package/dist/televet/index.js +1 -0
  124. package/dist/testing/index.cjs +5 -823
  125. package/dist/testing/index.d.cts +18 -4
  126. package/dist/testing/index.d.ts +18 -4
  127. package/dist/testing/index.js +5 -820
  128. package/dist/testing/rn/index.cjs +1 -449
  129. package/dist/testing/rn/index.d.cts +18 -29
  130. package/dist/testing/rn/index.d.ts +18 -29
  131. package/dist/testing/rn/index.js +1 -444
  132. package/dist/testing/web/index.cjs +1 -0
  133. package/dist/testing/web/index.d.cts +132 -0
  134. package/dist/testing/web/index.d.ts +132 -0
  135. package/dist/testing/web/index.js +1 -0
  136. package/dist/timelineRows-C15jxFFH.d.ts +19 -0
  137. package/dist/timelineRows-odO8g9r2.d.cts +19 -0
  138. package/dist/typeStyle-CKqVYD6L.d.cts +111 -0
  139. package/dist/typeStyle-DmoyacDG.d.ts +111 -0
  140. package/dist/uploadTransport-D0M0T4hN.d.ts +38 -0
  141. package/dist/uploadTransport-DKJHs3Yj.d.cts +38 -0
  142. package/dist/useRecordsDepth-C6rjEaLn.d.cts +237 -0
  143. package/dist/useRecordsDepth-crhBllQK.d.ts +237 -0
  144. package/dist/useVetVisit-Bc84hWUU.d.cts +187 -0
  145. package/dist/useVetVisit-CjmMa896.d.ts +187 -0
  146. package/dist/video/index.cjs +1 -1941
  147. package/dist/video/index.d.cts +4 -4
  148. package/dist/video/index.d.ts +4 -4
  149. package/dist/video/index.js +1 -1938
  150. package/dist/view-Bxwxp4xD.d.ts +310 -0
  151. package/dist/view-DyitEO0m.d.cts +310 -0
  152. package/dist/visitIntent-D7_yVp1I.d.cts +8 -0
  153. package/dist/visitIntent-D7_yVp1I.d.ts +8 -0
  154. package/dist/web/consent/index.cjs +1 -0
  155. package/dist/web/consent/index.d.cts +34 -0
  156. package/dist/web/consent/index.d.ts +34 -0
  157. package/dist/web/consent/index.js +1 -0
  158. package/dist/web/index.cjs +14 -0
  159. package/dist/web/index.d.cts +218 -0
  160. package/dist/web/index.d.ts +218 -0
  161. package/dist/web/index.js +14 -0
  162. package/dist/web/notifications/index.cjs +2 -0
  163. package/dist/web/notifications/index.d.cts +213 -0
  164. package/dist/web/notifications/index.d.ts +213 -0
  165. package/dist/web/notifications/index.js +2 -0
  166. package/dist/web/records/depth/index.cjs +2 -0
  167. package/dist/web/records/depth/index.d.cts +12 -0
  168. package/dist/web/records/depth/index.d.ts +12 -0
  169. package/dist/web/records/depth/index.js +2 -0
  170. package/dist/web/records/index.cjs +4 -0
  171. package/dist/web/records/index.d.cts +285 -0
  172. package/dist/web/records/index.d.ts +285 -0
  173. package/dist/web/records/index.js +4 -0
  174. package/dist/web/televet/booking/index.cjs +1 -0
  175. package/dist/web/televet/booking/index.d.cts +349 -0
  176. package/dist/web/televet/booking/index.d.ts +349 -0
  177. package/dist/web/televet/booking/index.js +1 -0
  178. package/dist/web/televet/call/index.cjs +1 -0
  179. package/dist/web/televet/call/index.d.cts +142 -0
  180. package/dist/web/televet/call/index.d.ts +142 -0
  181. package/dist/web/televet/call/index.js +1 -0
  182. package/dist/web/televet/index.cjs +1 -0
  183. package/dist/web/televet/index.d.cts +136 -0
  184. package/dist/web/televet/index.d.ts +136 -0
  185. package/dist/web/televet/index.js +1 -0
  186. package/notifications/package.json +8 -0
  187. package/package.json +266 -10
  188. package/records/depth/package.json +8 -0
  189. package/server/events/device-blocked.cjs +15 -0
  190. package/server/events/package.json +9 -0
  191. package/televet/booking/package.json +8 -0
  192. package/televet/call/package.json +8 -0
  193. package/televet/package.json +8 -0
  194. package/testing/web/native-blocked.cjs +12 -0
  195. package/testing/web/package.json +8 -0
  196. package/web/consent/native-blocked.cjs +12 -0
  197. package/web/consent/package.json +8 -0
  198. package/web/native-blocked.cjs +12 -0
  199. package/web/notifications/native-blocked.cjs +12 -0
  200. package/web/notifications/package.json +8 -0
  201. package/web/package.json +8 -0
  202. package/web/records/depth/native-blocked.cjs +12 -0
  203. package/web/records/depth/package.json +8 -0
  204. package/web/records/native-blocked.cjs +12 -0
  205. package/web/records/package.json +8 -0
  206. package/web/televet/booking/native-blocked.cjs +12 -0
  207. package/web/televet/booking/package.json +8 -0
  208. package/web/televet/call/native-blocked.cjs +12 -0
  209. package/web/televet/call/package.json +8 -0
  210. package/web/televet/native-blocked.cjs +12 -0
  211. package/web/televet/package.json +8 -0
  212. package/dist/ChatController-CKdBvPj2.d.ts +0 -146
  213. package/dist/ChatController-CpUMvvZf.d.cts +0 -146
  214. package/dist/FilePort-BabWrv7I.d.cts +0 -22
  215. package/dist/FilePort-BabWrv7I.d.ts +0 -22
  216. package/dist/petsRepository-BEGb97M9.d.cts +0 -326
  217. package/dist/petsRepository-Bu18r2kK.d.ts +0 -326
  218. package/dist/requestFunnel-DuUH-kAe.d.cts +0 -28
  219. package/dist/requestFunnel-dio5OmR9.d.ts +0 -28
@@ -0,0 +1,110 @@
1
+ import { c as EverfurResult, b as EverfurErrorCode } from './EverfurResult-DN9pL2Ab.cjs';
2
+ import { E as ErrorPolicyPort } from './ErrorPolicyPort-CNf4uQZP.cjs';
3
+ import { d as EverfurRequest, F as Frame } from './config--Wu1Umhd.cjs';
4
+ import { A as AuthContext } from './identity-C2-08BZW.cjs';
5
+
6
+ /** Why the funnel stopped, or kept going. Every settle and every retry names one. */
7
+ type FunnelRetryReason = 'ok' | 'aborted' | 'terminal-code' | 'attempts-exhausted' | 'daily-quota-exhausted' | 'burst-throttle' | 'retryable-code' | 'server-reset-beyond-budget';
8
+ /**
9
+ * One funnel observation. Codes, statuses and ids ONLY: `wireMessage` is untrusted, healthcare-adjacent
10
+ * free text and must never leave the SDK through a telemetry sink, so it is structurally absent here.
11
+ */
12
+ interface FunnelEvent {
13
+ /** Stable call-site name, e.g. `records.createRequest`. */
14
+ readonly call: string;
15
+ readonly phase: 'attempt' | 'retry' | 'settled' | 'stream.open';
16
+ readonly method: string;
17
+ readonly path: string;
18
+ /** 0-based attempt index. */
19
+ readonly attempt: number;
20
+ readonly ok?: boolean;
21
+ readonly code?: EverfurErrorCode;
22
+ readonly httpStatus?: number;
23
+ /** `X-Request-ID` of the failing response, when the server sent one. The support-ticket join key. */
24
+ readonly requestId?: string;
25
+ /** The client-minted key, when this request carried one. Never a caller-supplied value. */
26
+ readonly idempotencyKey?: string;
27
+ readonly reason?: FunnelRetryReason;
28
+ readonly delayMs?: number;
29
+ }
30
+ interface FunnelObserver {
31
+ onEvent(event: FunnelEvent): void;
32
+ }
33
+ /** The timer seam, mirroring the shape core already uses, so a test drives the deadline deterministically. */
34
+ interface FunnelTimers {
35
+ setTimeout(handler: () => void, ms: number): unknown;
36
+ clearTimeout(handle: unknown): void;
37
+ now(): number;
38
+ }
39
+ interface RequestFunnelDeps {
40
+ readonly auth: AuthContext;
41
+ readonly observer?: FunnelObserver;
42
+ readonly timers?: FunnelTimers;
43
+ /** Injected for deterministic jitter in tests. */
44
+ readonly rng?: () => number;
45
+ /** Injected so a test asserts the backoff schedule without burning wall-clock time. */
46
+ readonly sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
47
+ /** Total attempts (initial try plus retries) before a retryable failure surfaces. */
48
+ readonly maxAttempts?: number;
49
+ /**
50
+ * The partner's `EverfurConfig.errorPolicy`. Consulted per FAILURE, because both of its knobs are keyed
51
+ * on the error code, which is not known until one arrives. It can only NARROW what the funnel would do
52
+ * on its own: see `decideRetry`.
53
+ */
54
+ readonly errorPolicy?: ErrorPolicyPort;
55
+ readonly unaryTimeoutMs?: number;
56
+ /**
57
+ * Idempotency-key minter. Injected ONLY for deterministic tests. It is never reachable from partner
58
+ * code and never fed a caller-supplied value: the whole point is that the SDK owns the key.
59
+ */
60
+ readonly newIdempotencyKey?: () => string;
61
+ }
62
+ interface FunnelSendOptions {
63
+ /**
64
+ * Force the idempotency decision. Omit to derive it from the method (mutating -> minted). Pass `false`
65
+ * for a mutating request the server does not dedup and that must not claim to be idempotent.
66
+ */
67
+ readonly idempotent?: boolean;
68
+ /** Per-call attempt ceiling override (a poll loop that owns its own budget passes 1). */
69
+ readonly maxAttempts?: number;
70
+ /**
71
+ * The STABLE IDENTITY of this logical call, seeding the key the funnel mints for it.
72
+ *
73
+ * WHY A SEED AND NOT A HEADER. The funnel is the sole authority for `Idempotency-Key` because a caller
74
+ * that writes the header directly gets the guarantee backwards in both directions (see
75
+ * `assembleHeaders`). But the funnel only knows where one logical call starts and ends when the call is
76
+ * ONE `send`. Televet booking is not: the owner composes a draft across several screens, the key is
77
+ * derived from the held slot so a resubmit after a timeout is the SAME booking, and a screen re-mount
78
+ * mid-flow must not turn one booking into two. The caller is the only party that can say "this is still
79
+ * that booking", and it is the only thing it is allowed to say.
80
+ *
81
+ * SO THE SPLIT IS: the caller supplies the IDENTITY, the funnel still owns the HEADER. The seed is
82
+ * namespaced and length-bounded here rather than forwarded verbatim, so a caller cannot smuggle a key
83
+ * that collides with a minted one, exceed the server's 8..128 bound, or reuse a value from another
84
+ * surface. Everything `assembleHeaders` strips, it still strips: a caller-supplied header is never
85
+ * honoured, seed or no seed.
86
+ *
87
+ * IGNORED when the call is not idempotent (`idempotent: false` or a GET), because there is no key to
88
+ * seed. A blank or whitespace-only seed falls back to a minted key rather than sending nothing: an
89
+ * empty identity is a caller bug, and dropping a REQUIRED header over it would turn it into a 422 the
90
+ * owner reads as "booking failed".
91
+ */
92
+ readonly idempotencyKey?: string;
93
+ }
94
+ interface RequestFunnel {
95
+ /** The ONE unary path: headers, auth, deadline, retry, idempotency, request-id, telemetry. */
96
+ send<T>(call: string, req: EverfurRequest, opts?: FunnelSendOptions): Promise<EverfurResult<T>>;
97
+ /**
98
+ * Resolve device auth onto a request (bearer XOR publishable key, caller auth headers stripped).
99
+ * Rejects only when the TokenProvider fails; the caller settles that into its own terminal outcome.
100
+ */
101
+ authorize(req: EverfurRequest): Promise<EverfurRequest>;
102
+ /**
103
+ * Open an SSE stream. NOT idempotency-wrapped and NOT buffered: frames are yielded as they arrive.
104
+ * `req` is expected to have been through `authorize`.
105
+ */
106
+ stream(call: string, req: EverfurRequest, signal: AbortSignal): AsyncIterable<Frame>;
107
+ }
108
+ declare function createRequestFunnel(deps: RequestFunnelDeps): RequestFunnel;
109
+
110
+ export { type FunnelSendOptions as F, type RequestFunnel as R, type RequestFunnelDeps as a, createRequestFunnel as c };
@@ -0,0 +1,110 @@
1
+ import { c as EverfurResult, b as EverfurErrorCode } from './EverfurResult-DN9pL2Ab.js';
2
+ import { E as ErrorPolicyPort } from './ErrorPolicyPort-CwbfmxzJ.js';
3
+ import { d as EverfurRequest, F as Frame } from './config-C7eb_IMD.js';
4
+ import { A as AuthContext } from './identity-CEi9GRFZ.js';
5
+
6
+ /** Why the funnel stopped, or kept going. Every settle and every retry names one. */
7
+ type FunnelRetryReason = 'ok' | 'aborted' | 'terminal-code' | 'attempts-exhausted' | 'daily-quota-exhausted' | 'burst-throttle' | 'retryable-code' | 'server-reset-beyond-budget';
8
+ /**
9
+ * One funnel observation. Codes, statuses and ids ONLY: `wireMessage` is untrusted, healthcare-adjacent
10
+ * free text and must never leave the SDK through a telemetry sink, so it is structurally absent here.
11
+ */
12
+ interface FunnelEvent {
13
+ /** Stable call-site name, e.g. `records.createRequest`. */
14
+ readonly call: string;
15
+ readonly phase: 'attempt' | 'retry' | 'settled' | 'stream.open';
16
+ readonly method: string;
17
+ readonly path: string;
18
+ /** 0-based attempt index. */
19
+ readonly attempt: number;
20
+ readonly ok?: boolean;
21
+ readonly code?: EverfurErrorCode;
22
+ readonly httpStatus?: number;
23
+ /** `X-Request-ID` of the failing response, when the server sent one. The support-ticket join key. */
24
+ readonly requestId?: string;
25
+ /** The client-minted key, when this request carried one. Never a caller-supplied value. */
26
+ readonly idempotencyKey?: string;
27
+ readonly reason?: FunnelRetryReason;
28
+ readonly delayMs?: number;
29
+ }
30
+ interface FunnelObserver {
31
+ onEvent(event: FunnelEvent): void;
32
+ }
33
+ /** The timer seam, mirroring the shape core already uses, so a test drives the deadline deterministically. */
34
+ interface FunnelTimers {
35
+ setTimeout(handler: () => void, ms: number): unknown;
36
+ clearTimeout(handle: unknown): void;
37
+ now(): number;
38
+ }
39
+ interface RequestFunnelDeps {
40
+ readonly auth: AuthContext;
41
+ readonly observer?: FunnelObserver;
42
+ readonly timers?: FunnelTimers;
43
+ /** Injected for deterministic jitter in tests. */
44
+ readonly rng?: () => number;
45
+ /** Injected so a test asserts the backoff schedule without burning wall-clock time. */
46
+ readonly sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
47
+ /** Total attempts (initial try plus retries) before a retryable failure surfaces. */
48
+ readonly maxAttempts?: number;
49
+ /**
50
+ * The partner's `EverfurConfig.errorPolicy`. Consulted per FAILURE, because both of its knobs are keyed
51
+ * on the error code, which is not known until one arrives. It can only NARROW what the funnel would do
52
+ * on its own: see `decideRetry`.
53
+ */
54
+ readonly errorPolicy?: ErrorPolicyPort;
55
+ readonly unaryTimeoutMs?: number;
56
+ /**
57
+ * Idempotency-key minter. Injected ONLY for deterministic tests. It is never reachable from partner
58
+ * code and never fed a caller-supplied value: the whole point is that the SDK owns the key.
59
+ */
60
+ readonly newIdempotencyKey?: () => string;
61
+ }
62
+ interface FunnelSendOptions {
63
+ /**
64
+ * Force the idempotency decision. Omit to derive it from the method (mutating -> minted). Pass `false`
65
+ * for a mutating request the server does not dedup and that must not claim to be idempotent.
66
+ */
67
+ readonly idempotent?: boolean;
68
+ /** Per-call attempt ceiling override (a poll loop that owns its own budget passes 1). */
69
+ readonly maxAttempts?: number;
70
+ /**
71
+ * The STABLE IDENTITY of this logical call, seeding the key the funnel mints for it.
72
+ *
73
+ * WHY A SEED AND NOT A HEADER. The funnel is the sole authority for `Idempotency-Key` because a caller
74
+ * that writes the header directly gets the guarantee backwards in both directions (see
75
+ * `assembleHeaders`). But the funnel only knows where one logical call starts and ends when the call is
76
+ * ONE `send`. Televet booking is not: the owner composes a draft across several screens, the key is
77
+ * derived from the held slot so a resubmit after a timeout is the SAME booking, and a screen re-mount
78
+ * mid-flow must not turn one booking into two. The caller is the only party that can say "this is still
79
+ * that booking", and it is the only thing it is allowed to say.
80
+ *
81
+ * SO THE SPLIT IS: the caller supplies the IDENTITY, the funnel still owns the HEADER. The seed is
82
+ * namespaced and length-bounded here rather than forwarded verbatim, so a caller cannot smuggle a key
83
+ * that collides with a minted one, exceed the server's 8..128 bound, or reuse a value from another
84
+ * surface. Everything `assembleHeaders` strips, it still strips: a caller-supplied header is never
85
+ * honoured, seed or no seed.
86
+ *
87
+ * IGNORED when the call is not idempotent (`idempotent: false` or a GET), because there is no key to
88
+ * seed. A blank or whitespace-only seed falls back to a minted key rather than sending nothing: an
89
+ * empty identity is a caller bug, and dropping a REQUIRED header over it would turn it into a 422 the
90
+ * owner reads as "booking failed".
91
+ */
92
+ readonly idempotencyKey?: string;
93
+ }
94
+ interface RequestFunnel {
95
+ /** The ONE unary path: headers, auth, deadline, retry, idempotency, request-id, telemetry. */
96
+ send<T>(call: string, req: EverfurRequest, opts?: FunnelSendOptions): Promise<EverfurResult<T>>;
97
+ /**
98
+ * Resolve device auth onto a request (bearer XOR publishable key, caller auth headers stripped).
99
+ * Rejects only when the TokenProvider fails; the caller settles that into its own terminal outcome.
100
+ */
101
+ authorize(req: EverfurRequest): Promise<EverfurRequest>;
102
+ /**
103
+ * Open an SSE stream. NOT idempotency-wrapped and NOT buffered: frames are yielded as they arrive.
104
+ * `req` is expected to have been through `authorize`.
105
+ */
106
+ stream(call: string, req: EverfurRequest, signal: AbortSignal): AsyncIterable<Frame>;
107
+ }
108
+ declare function createRequestFunnel(deps: RequestFunnelDeps): RequestFunnel;
109
+
110
+ export { type FunnelSendOptions as F, type RequestFunnel as R, type RequestFunnelDeps as a, createRequestFunnel as c };
@@ -2,7 +2,7 @@
2
2
  * Every entitlement key the PUBLIC contract declares. Internal packs are absent by construction (the
3
3
  * projection redacts them), so this union is exactly what a partner surface may require.
4
4
  */
5
- type EverfurCapabilityKey = "animations.state.read" | "chat.conversation.read" | "chat.message.create" | "photo.checkup.execute" | "records.record.export" | "records.record.read" | "records.record.share" | "records.upload.create" | "video.gait.execute";
5
+ type EverfurCapabilityKey = "animations.state.read" | "chat.conversation.read" | "chat.message.create" | "partner_events.event.read" | "partner_events.inbound.create" | "pets.health.update" | "pets.profile.delete" | "pets.profile.update" | "photo.checkup.execute" | "records.clinic.create" | "records.record.create" | "records.record.export" | "records.record.read" | "records.record.share" | "records.upload.create" | "televet.visit.create" | "video.gait.execute";
6
6
 
7
7
  interface LevelGrant {
8
8
  /** capability keys this level permits */
@@ -24,7 +24,7 @@ interface ResolvedEntitlements {
24
24
  */
25
25
  declare function resolveEntitlements(levels: readonly LevelGrant[]): ResolvedEntitlements;
26
26
  /** Coarse UI capability ids. NOT valid entitlement keys; bridged to resource keys by `requires`. */
27
- type CapabilityName = 'chat' | 'records' | 'video' | 'photo' | 'animations';
27
+ type CapabilityName = 'chat' | 'records' | 'recordsClinic' | 'video' | 'photo' | 'animations' | 'televet';
28
28
  type DisabledLevel = 'contract' | 'partner_config' | 'user_entitlement' | 'version_floor' | 'kill_switch'
29
29
  /**
30
30
  * The SDK itself is not configured (no publishableKey, or no resolvable apiBaseUrl), so NOTHING can be
@@ -72,7 +72,13 @@ declare function requires(name: CapabilityName): readonly EverfurCapabilityKey[]
72
72
  */
73
73
  declare function deniedDecisionsFor(level: DisabledLevel, reason: string): readonly EntitlementDecision[];
74
74
  interface DecideOptions {
75
- /** true during first resolve or while serving a stale value -> skeleton, never a flash of enabled UI. */
75
+ /**
76
+ * The first verdict of the scope is still being fetched. It pends ONLY a capability the decision set leaves
77
+ * undecided (a required key absent from it): a gate then shows its skeleton rather than the fail-closed
78
+ * off-state it would have to retract when the verdict lands. A capability every key of which the set already
79
+ * decides renders that decision at once, granted (chat, on the compiled floor) or denied (every key of a
80
+ * disabled runtime), so a verdict in flight never adds a loading step to what the floor has settled.
81
+ */
76
82
  readonly isPending?: boolean;
77
83
  }
78
84
  /**
@@ -83,4 +89,4 @@ interface DecideOptions {
83
89
  */
84
90
  declare function decideCapability(name: CapabilityName, decisions: readonly EntitlementDecision[], opts?: DecideOptions): CapabilityDecision;
85
91
 
86
- export { type CapabilityDecision as C, type DecideOptions as D, type EntitlementDecision as E, type LevelGrant as L, type ResolvedEntitlements as R, type CapabilityDisabled as a, type CapabilityName as b, type DisabledLevel as c, decideCapability as d, deniedDecisionsFor as e, resolveEntitlements as f, requires as r };
92
+ export { type CapabilityName as C, type DisabledLevel as D, type EntitlementDecision as E, type LevelGrant as L, type ResolvedEntitlements as R, type CapabilityDecision as a, type CapabilityDisabled as b, type DecideOptions as c, decideCapability as d, deniedDecisionsFor as e, resolveEntitlements as f, requires as r };
@@ -2,7 +2,7 @@
2
2
  * Every entitlement key the PUBLIC contract declares. Internal packs are absent by construction (the
3
3
  * projection redacts them), so this union is exactly what a partner surface may require.
4
4
  */
5
- type EverfurCapabilityKey = "animations.state.read" | "chat.conversation.read" | "chat.message.create" | "photo.checkup.execute" | "records.record.export" | "records.record.read" | "records.record.share" | "records.upload.create" | "video.gait.execute";
5
+ type EverfurCapabilityKey = "animations.state.read" | "chat.conversation.read" | "chat.message.create" | "partner_events.event.read" | "partner_events.inbound.create" | "pets.health.update" | "pets.profile.delete" | "pets.profile.update" | "photo.checkup.execute" | "records.clinic.create" | "records.record.create" | "records.record.export" | "records.record.read" | "records.record.share" | "records.upload.create" | "televet.visit.create" | "video.gait.execute";
6
6
 
7
7
  interface LevelGrant {
8
8
  /** capability keys this level permits */
@@ -24,7 +24,7 @@ interface ResolvedEntitlements {
24
24
  */
25
25
  declare function resolveEntitlements(levels: readonly LevelGrant[]): ResolvedEntitlements;
26
26
  /** Coarse UI capability ids. NOT valid entitlement keys; bridged to resource keys by `requires`. */
27
- type CapabilityName = 'chat' | 'records' | 'video' | 'photo' | 'animations';
27
+ type CapabilityName = 'chat' | 'records' | 'recordsClinic' | 'video' | 'photo' | 'animations' | 'televet';
28
28
  type DisabledLevel = 'contract' | 'partner_config' | 'user_entitlement' | 'version_floor' | 'kill_switch'
29
29
  /**
30
30
  * The SDK itself is not configured (no publishableKey, or no resolvable apiBaseUrl), so NOTHING can be
@@ -72,7 +72,13 @@ declare function requires(name: CapabilityName): readonly EverfurCapabilityKey[]
72
72
  */
73
73
  declare function deniedDecisionsFor(level: DisabledLevel, reason: string): readonly EntitlementDecision[];
74
74
  interface DecideOptions {
75
- /** true during first resolve or while serving a stale value -> skeleton, never a flash of enabled UI. */
75
+ /**
76
+ * The first verdict of the scope is still being fetched. It pends ONLY a capability the decision set leaves
77
+ * undecided (a required key absent from it): a gate then shows its skeleton rather than the fail-closed
78
+ * off-state it would have to retract when the verdict lands. A capability every key of which the set already
79
+ * decides renders that decision at once, granted (chat, on the compiled floor) or denied (every key of a
80
+ * disabled runtime), so a verdict in flight never adds a loading step to what the floor has settled.
81
+ */
76
82
  readonly isPending?: boolean;
77
83
  }
78
84
  /**
@@ -83,4 +89,4 @@ interface DecideOptions {
83
89
  */
84
90
  declare function decideCapability(name: CapabilityName, decisions: readonly EntitlementDecision[], opts?: DecideOptions): CapabilityDecision;
85
91
 
86
- export { type CapabilityDecision as C, type DecideOptions as D, type EntitlementDecision as E, type LevelGrant as L, type ResolvedEntitlements as R, type CapabilityDisabled as a, type CapabilityName as b, type DisabledLevel as c, decideCapability as d, deniedDecisionsFor as e, resolveEntitlements as f, requires as r };
92
+ export { type CapabilityName as C, type DisabledLevel as D, type EntitlementDecision as E, type LevelGrant as L, type ResolvedEntitlements as R, type CapabilityDecision as a, type CapabilityDisabled as b, type DecideOptions as c, decideCapability as d, deniedDecisionsFor as e, resolveEntitlements as f, requires as r };
@@ -1,14 +1,16 @@
1
- import { E as EntitlementDecision, b as CapabilityName } from './resolve-Dq_4_agU.cjs';
2
- import { C as ClientBranding, F as FetchEntitlementsArgs, c as EntitlementResolution, w as PetProfileInput, R as RegisteredPet } from './petsRepository-BEGb97M9.cjs';
3
- import { E as EverfurResult } from './EverfurResult-D92-uL82.cjs';
4
- import { A as AuthContext, T as TokenProvider } from './identity-Brl-lDd6.cjs';
5
- import { U as UserRef, P as PetRef, a as ConversationId } from './ids-CJ1S6adf.cjs';
6
- import { T as TransportPort, k as EventSourceFactory } from './config-BSjBdxrZ.cjs';
1
+ import { E as EntitlementDecision, C as CapabilityName } from './resolve-D4Ywz5OS.cjs';
2
+ import { P as PetProfileInput, R as RegisteredPet, a as PetProfilePatch } from './petsRepository-B_gKbAzJ.cjs';
3
+ import { c as EverfurResult } from './EverfurResult-DN9pL2Ab.cjs';
4
+ import { E as EverfurThemeInput, C as ClientBranding } from './branding-CsRdeNwK.cjs';
5
+ import { A as AuthContext, T as TokenProvider } from './identity-C2-08BZW.cjs';
6
+ import { U as UserRef, P as PetRef, C as ConversationId } from './ids-B2GAAifq.cjs';
7
+ import { T as TransportPort } from './config--Wu1Umhd.cjs';
7
8
  import { I as ImageCachePort, K as KvPort } from './cacheEpoch-DknKn3S0.cjs';
8
- import { T as TelemetryPort } from './TelemetryPort-BDNr00hu.cjs';
9
- import { E as ErrorPolicyPort, C as ChatController } from './ChatController-CpUMvvZf.cjs';
10
- import { F as FileHandle } from './FilePort-BabWrv7I.cjs';
9
+ import { E as ErrorPolicyPort } from './ErrorPolicyPort-CNf4uQZP.cjs';
10
+ import { U as UploadPort, l as ChatController } from './ports-BY2ph0_y.cjs';
11
11
  import { C as CameraPort } from './CameraPort-Cv31Pz7g.cjs';
12
+ import { T as TelemetryPort } from './TelemetryPort-BDNr00hu.cjs';
13
+ import { U as UploadTransport } from './ports-BN6RHF9W.cjs';
12
14
 
13
15
  /**
14
16
  * Opt-in encrypted secret storage. Backed by a Keychain / Keystore native peer through an rn-layer adapter.
@@ -50,95 +52,6 @@ interface LogSink {
50
52
  emit(record: Readonly<LogRecord>): void;
51
53
  }
52
54
 
53
- /** Minimum re-poll interval; a server ttl below this is clamped so we never hot-loop the endpoint. */
54
- declare const MIN_POLL_INTERVAL_SECONDS = 30;
55
- /** Injectable timer seam so a suite drives the re-poll clock deterministically (default: global timers). */
56
- type TimerHandle = ReturnType<typeof setTimeout>;
57
- interface PollScheduler {
58
- set(callback: () => void, ms: number): TimerHandle;
59
- clear(handle: TimerHandle): void;
60
- }
61
- interface EntitlementPollerDeps {
62
- /** Re-derived every poll so a token refresh / user re-scope is reflected on the next hop. */
63
- readonly authFor: () => AuthContext;
64
- /**
65
- * Push a fresh verdict into the runtime store. Called ONLY on a 200 'updated'. Carries the server-driven
66
- * branding parsed from the SAME body (null when absent/unparseable -> the compiled default theme); branding
67
- * is additive and orthogonal to the decisions.
68
- */
69
- readonly update: (decisions: readonly EntitlementDecision[], branding: ClientBranding | null) => void;
70
- /** Optional region hint forwarded to the endpoint. */
71
- readonly region?: string;
72
- readonly telemetry?: TelemetryPort;
73
- /** Test seam; defaults to global timers (unref'd so a live poll never pins a Node event loop). */
74
- readonly scheduler?: PollScheduler;
75
- /** Test seam; defaults to the real repository call. */
76
- readonly resolve?: (auth: AuthContext, args: FetchEntitlementsArgs) => Promise<EntitlementResolution>;
77
- /** Floor override for tests; defaults to MIN_POLL_INTERVAL_SECONDS. */
78
- readonly floorSeconds?: number;
79
- /** Test seam for deterministic failure-retry jitter; defaults to Math.random. */
80
- readonly rng?: () => number;
81
- }
82
- interface EntitlementPoller {
83
- /** Idempotent. Kicks an immediate first poll, then self-schedules on the returned cadence. */
84
- start(): void;
85
- /** Idempotent. Clears the pending timer and prevents any post-stop update (dispose calls this). */
86
- stop(): void;
87
- /**
88
- * Drop the cached revision and re-poll immediately. A user / pet re-scope invalidates the cached verdict, so
89
- * the next request must be UNCONDITIONAL (no If-None-Match) to force a fresh 200 for the new scope.
90
- */
91
- refresh(): void;
92
- }
93
- declare function createEntitlementPoller(deps: EntitlementPollerDeps): EntitlementPoller;
94
-
95
- /** The presigned POST/PUT policy returned by `/uploads/initiate` (SPEC-11b §2.1). */
96
- interface PresignedPolicy {
97
- /** Absolute S3 endpoint URL to POST/PUT the bytes at. */
98
- readonly url: string;
99
- /** POST-policy form fields to include with the upload (empty for a plain PUT). */
100
- readonly fields: Readonly<Record<string, string>>;
101
- /** The owned-uploads-prefixed key the analyze call then references. */
102
- readonly s3Key: string;
103
- /** epoch seconds the policy expires; telemetry only. */
104
- readonly expiresAt: number | null;
105
- }
106
- /**
107
- * The native upload seam. It PUTs `file` at the presigned `target` (SPEC-11b §2.1). `idempotencyKey` is reused
108
- * on every retry so a re-uploaded identical file dedupes server-side. A network failure REJECTS; the controller
109
- * normalizes that to a `mediaUploadFailed` and exposes a retake affordance (never auto-retried, SPEC-00 §3.2).
110
- *
111
- * NOTE: the spine `FilePort.presignedPut(file, { idempotencyKey, signal })` has no slot for the presigned
112
- * target; this richer port is the shape it must grow to. See the manifest gap note.
113
- */
114
- interface UploadPort {
115
- presignedPut(file: FileHandle, opts: {
116
- readonly target: PresignedPolicy;
117
- readonly idempotencyKey: string;
118
- readonly signal?: AbortSignal;
119
- }): Promise<void>;
120
- }
121
-
122
- interface UploadPolicyWire {
123
- readonly upload_id: string;
124
- readonly upload_url: string;
125
- readonly upload_fields: Readonly<Record<string, string>>;
126
- readonly expires_in_seconds: number;
127
- readonly max_content_length: number;
128
- }
129
- /**
130
- * Platform-specific: the rn adapter binds `expo-file-system` (uploadAsync) behind `optionalModule`; the
131
- * controller stays platform-neutral and never sees a native module. Resolves on S3's 204; rejects on a
132
- * policy 403 / stream failure, which the controller normalizes to a typed `mediaUploadFailed` (never a throw
133
- * that escapes the controller).
134
- */
135
- interface UploadTransport {
136
- postMultipart(policy: UploadPolicyWire, file: FileHandle, opts: {
137
- readonly onProgress?: (fraction: number) => void;
138
- readonly signal?: AbortSignal;
139
- }): Promise<void>;
140
- }
141
-
142
55
  /**
143
56
  * The public init config the host passes at EverfurProvider mount: the lean client subset
144
57
  * (publishableKey / apiBaseUrl / contractVersion / transport / debug) PLUS the host-seam ports. The client
@@ -189,8 +102,8 @@ interface RuntimeUser {
189
102
  readonly userRef: UserRef;
190
103
  readonly getToken: TokenProvider;
191
104
  }
192
- /** Opaque, permissive theme input; server branding merges ON TOP. Sanitized downstream. */
193
- type RuntimeTheme = Readonly<Record<string, string | number | boolean>>;
105
+ /** The client `theme` prop as the provider hands it over (tolerant shape; sanitized by the theme provider). */
106
+ type RuntimeTheme = EverfurThemeInput;
194
107
  interface CreateEverfurRuntimeInput {
195
108
  readonly config: EverfurRuntimeConfig;
196
109
  /** null => general (anonymous) mode: pk alone on the wire. */
@@ -221,6 +134,12 @@ interface CapabilityServices {
221
134
  readonly uploadTransport: UploadTransport;
222
135
  readonly imageCache: ImageCachePort;
223
136
  readonly kv: KvPort;
137
+ /**
138
+ * The API base URL the transport was built for (the config's, or the environment's), or null with an
139
+ * injected transport and no base URL. A relative link the server returns (a share link's path) is
140
+ * resolved against this origin, never opened raw.
141
+ */
142
+ readonly apiBaseUrl: string | null;
224
143
  }
225
144
  /**
226
145
  * The ONE ref-counted capability registry (SPEC-10 §7.4). Chat (the spine) is acquired by its typed method;
@@ -236,6 +155,12 @@ interface CapabilityRegistry {
236
155
  /** Chat is the spine capability; the registry constructs + ref-counts it directly. */
237
156
  acquireChat(scope: ChatScope): ChatController;
238
157
  releaseChat(controller: ChatController): void;
158
+ /**
159
+ * The live controller for a scope WITHOUT taking a hold, or null. A mount reads it synchronously on its
160
+ * first render so a scope `prepare()` primed renders its settled snapshot at once (no bootstrapping frame),
161
+ * then takes its hold through `acquireChat` in the commit effect as before.
162
+ */
163
+ peekChat(scope: ChatScope): ChatController | null;
239
164
  /**
240
165
  * Ref-counted acquire for the tree-shakeable capabilities. Two calls with the same (kind, scopeKey) under
241
166
  * the current epoch share ONE controller; the last release disposes it via `destroy`.
@@ -249,7 +174,12 @@ type ChatControllerRegistry = CapabilityRegistry;
249
174
  /** The already-resolved server verdict the capability hooks render (never re-folded here). */
250
175
  interface EntitlementSnapshot {
251
176
  readonly decisions: readonly EntitlementDecision[];
252
- /** true during the first resolve or while serving a stale value -> skeleton, never a flash of UI. */
177
+ /**
178
+ * The first verdict of the current scope is still being fetched. `decideCapability` pends only what the
179
+ * decisions leave undecided: a key absent from the compiled floor (records) shows its skeleton, a key the
180
+ * floor grants (chat) renders at once. false once that fetch settles, whatever it settled to (a failure keeps
181
+ * the floor: fail-closed, never a skeleton left up), and always on a runtime that never fetched.
182
+ */
253
183
  readonly isPending: boolean;
254
184
  /**
255
185
  * Server-driven theme branding from the SAME entitlements body (orthogonal to decisions). null/absent =>
@@ -267,6 +197,48 @@ interface EntitlementStore {
267
197
  subscribe(listener: () => void): () => void;
268
198
  getSnapshot(): EntitlementSnapshot;
269
199
  }
200
+ interface PrepareOptions {
201
+ /**
202
+ * The pet the first chat mount will be scoped to. Names it and the warm-up ALSO primes that scope's chat
203
+ * bootstrap (conversation + suggested prompts), held for the first mount. It registers nothing: a pet the
204
+ * partner has not upserted is registered with `registerPet`, not here.
205
+ */
206
+ readonly petRef?: PetRef;
207
+ }
208
+ /**
209
+ * `ready`: token minted (when a user is set), verdict resolved, chat primed (when a pet was named).
210
+ * `partial`: the token is ready but the verdict fetch or the chat prime settled to an error; surfaces still
211
+ * mount and self-heal (the poller re-polls, the first send recreates the conversation).
212
+ * `failed`: nothing authenticated can proceed: the mint failed, or the runtime is disposed / not configured.
213
+ */
214
+ type PrepareStatus = 'ready' | 'partial' | 'failed';
215
+ /** The settled warm-up. Each step carries the same `EverfurError` the surfaces settle to; nothing throws. */
216
+ interface PrepareResult {
217
+ readonly status: PrepareStatus;
218
+ /** Milliseconds from the `prepare()` call to settlement: the login-to-ready number, also on telemetry. */
219
+ readonly durationMs: number;
220
+ /** The session-token mint. `ok` without a user (anonymous mode mints nothing). */
221
+ readonly session: EverfurResult<void>;
222
+ /** The entitlement verdict fetch into the store every gate reads. */
223
+ readonly entitlements: EverfurResult<void>;
224
+ /** The chat prime for `petRef`; absent when no pet was named. */
225
+ readonly chat?: EverfurResult<void>;
226
+ }
227
+ /**
228
+ * What a before-logout hook receives: who is leaving, and an auth context that keeps signing requests for the
229
+ * length of the hook budget even though the runtime drops the live identity synchronously right after the hooks
230
+ * start. The bearer is read from the identity's cache at that instant (or minted then, if none was cached), so a
231
+ * request the hook issues after an `await` still goes out under the signed-out user, never re-minted afterwards.
232
+ */
233
+ interface BeforeLogoutContext {
234
+ readonly userRef: UserRef;
235
+ readonly auth: AuthContext;
236
+ }
237
+ /**
238
+ * A hook that runs when a signed-in user is about to leave (`logout()`, `setUser(null)`, a switch to another
239
+ * user). It may return a promise; `logout()` waits for it up to `LOGOUT_HOOK_BUDGET_MS` and never fails on it.
240
+ */
241
+ type BeforeLogoutHook = (context: BeforeLogoutContext) => unknown;
270
242
  interface EverfurRuntime {
271
243
  readonly registry: CapabilityRegistry;
272
244
  /**
@@ -303,18 +275,39 @@ interface EverfurRuntime {
303
275
  * calling `setUser(nextUser)`.
304
276
  */
305
277
  logout(): Promise<void>;
278
+ /**
279
+ * Run `hook` BEFORE a signed-in user leaves: on `logout()`, on `setUser(null)` and on a switch to another user,
280
+ * with the leaving user's `userRef` and a grace auth that still signs requests. Started synchronously, before
281
+ * the bearer drops and the capabilities re-scope; `logout()` additionally awaits it, bounded by
282
+ * `LOGOUT_HOOK_BUDGET_MS`, and a hook that throws, rejects or hangs never fails or delays sign-out beyond that.
283
+ * The per-user KV sweep runs after the hooks. Returns the unsubscribe. Nothing runs for an anonymous runtime.
284
+ */
285
+ onBeforeLogout(hook: BeforeLogoutHook): () => void;
306
286
  /** Re-scopes pet-bound controllers in the same commit. */
307
287
  setActivePet(pet: PetRef | null): void;
308
288
  /**
309
289
  * Register (or update) the widget pet profile the records and consent routes assume already exists.
310
290
  *
311
291
  * All three consent routes resolve `pet_ref` through the server's `_resolve_pet_id`, which 404s when no
312
- * widget pet exists — so consent, and therefore records, is UNREACHABLE for any pet the partner has not
292
+ * widget pet exists, so consent, and therefore records, is UNREACHABLE for any pet the partner has not
313
293
  * upserted out of band. `createPetsRepository` exists on the `@everfur/sdk/client` subpath, but that is
314
294
  * the advanced-integrator surface and it takes a raw `AuthContext`; a partner using the provider had no
315
295
  * way to call it. This is that way. Idempotent: the route is an upsert keyed on `pet_ref`.
316
296
  */
317
297
  registerPet(pet: PetRef, profile?: PetProfileInput): Promise<EverfurResult<RegisteredPet>>;
298
+ /**
299
+ * Change part of a registered pet's profile (`PATCH /widget/v1/pets/{pet_ref}`). An omitted field is unchanged,
300
+ * `null` clears it. An unknown pet settles to the not-found error.
301
+ */
302
+ updatePet(pet: PetRef, patch: PetProfilePatch): Promise<EverfurResult<RegisteredPet>>;
303
+ /** Delete a pet profile (`DELETE /widget/v1/pets/{pet_ref}`); the server also retires its Records data. */
304
+ deletePet(pet: PetRef): Promise<EverfurResult<void>>;
305
+ /**
306
+ * Permanently erase everything Everfur holds for the current user (`DELETE /widget/v1/me`). A failure is
307
+ * retryable and a repeat call resumes the erasure. It does not sign the user out: call `logout()` or
308
+ * `setUser(null)` afterwards so mounted surfaces stop showing the erased data.
309
+ */
310
+ eraseUserData(): Promise<EverfurResult<void>>;
318
311
  /**
319
312
  * Replace the rendered entitlement verdict (a future poller / a manifest refresh). The SERVER stays
320
313
  * authoritative: this only feeds the render store, so the pending/enabled paths become reachable while a
@@ -323,40 +316,19 @@ interface EverfurRuntime {
323
316
  updateEntitlements(next: EntitlementSnapshot): void;
324
317
  /** Navigate / mount a prebuilt capability screen (host-routed). No-op at the spine. */
325
318
  open(capability: CapabilityName): void;
319
+ /**
320
+ * Warm up during the partner's OWN login so no Everfur surface adds a loading step of its own: mint the
321
+ * session token, resolve the entitlement verdict into the gate store and, when a pet is named, prime its
322
+ * chat bootstrap. Concurrent calls coalesce onto one run; a settled `ready` is returned again without I/O
323
+ * until the identity or pet re-scopes; a `partial` / `failed` run is retried on the next call. Never
324
+ * throws. Emits `sdk.prepare.timing` (`durationMs`, `status`) on the host telemetry sink once per run.
325
+ */
326
+ prepare(opts?: PrepareOptions): Promise<PrepareResult>;
326
327
  /**
327
328
  * Idempotent teardown: aborts in-flight capability requests, tears down every open SSE stream, and stops
328
329
  * the entitlement poller (an in-flight entitlement poll's result is DROPPED, not aborted mid-flight).
329
330
  */
330
331
  dispose(): void;
331
332
  }
332
- /** Runtime-construction options, distinct from the partner-facing config (host wiring / test seams). */
333
- interface CreateRuntimeOptions {
334
- /**
335
- * Whether to start the entitlement poller. Default: start ONLY when the transport was NOT injected (a real,
336
- * base-URL runtime). An injected transport is the test seam, so auto-start stays OFF to keep MockTransport
337
- * suites network-free; pass `true` to exercise the wired poller against a MockTransport.
338
- */
339
- readonly startEntitlementPolling?: boolean;
340
- /** Test seam: an injected scheduler so a suite can drive the entitlement re-poll timer deterministically. */
341
- readonly pollScheduler?: PollScheduler;
342
- /**
343
- * Host-wiring seam: the EventSource factory the built HTTP transport falls back to when a streaming
344
- * `res.body` is unavailable (stock RN fetch). The rn facade injects react-native-sse here so streaming works
345
- * on stock React Native; core NEVER imports react-native-sse (it stays framework-free and only threads this
346
- * through). Absent (web/expo, or an injected transport) => the fetch-body streaming path is used.
347
- */
348
- readonly eventSourceFactory?: EventSourceFactory;
349
- }
350
- /**
351
- * Construct the runtime once (EverfurProvider mount). Builds the transport (injected test seam, else the
352
- * real HTTP transport from the resolved client config), the IdentityProvider when a user is set, the
353
- * capability registry, the entitlement store, and (for a real runtime) the entitlement poller.
354
- * A config that cannot be resolved (missing publishableKey, or an unresolvable / non-https base URL when
355
- * no transport is injected) does NOT throw: it yields a DISABLED runtime whose transport refuses and
356
- * whose every capability settles to the deliberate off-state at `disabled.level === 'sdk_config'`.
357
- * @throws {EverfurConfigError} only for a supplied ACTIVE secureStore, which expresses a security
358
- * intent the SDK cannot yet honour and must not silently discard.
359
- */
360
- declare function createEverfurRuntime(input: CreateEverfurRuntimeInput, opts?: CreateRuntimeOptions): EverfurRuntime;
361
333
 
362
- export { type CapabilityKind as C, type EntitlementPoller as E, type LogLevel as L, MIN_POLL_INTERVAL_SECONDS as M, NULL_SECURE_STORE as N, type PollScheduler as P, type RuntimeTheme as R, type SecureStorePort as S, type TimerHandle as T, type UploadPort as U, type CapabilityRegistry as a, type CapabilityServices as b, type ChatControllerRegistry as c, type ChatScope as d, type CreateEverfurRuntimeInput as e, type CreateRuntimeOptions as f, type EntitlementPollerDeps as g, type EntitlementSnapshot as h, type EntitlementStore as i, type EverfurRuntime as j, type EverfurRuntimeConfig as k, type LogRecord as l, type LogSink as m, type RuntimeUser as n, createEntitlementPoller as o, createEverfurRuntime as p, type UploadTransport as q, type PresignedPolicy as r, type UploadPolicyWire as s };
334
+ export { type BeforeLogoutContext as B, type CreateEverfurRuntimeInput as C, type EntitlementSnapshot as E, type LogLevel as L, NULL_SECURE_STORE as N, type PrepareOptions as P, type RuntimeTheme as R, type SecureStorePort as S, type LogRecord as a, type LogSink as b, type PrepareResult as c, type PrepareStatus as d, type EverfurRuntime as e, type BeforeLogoutHook as f, type CapabilityKind as g, type CapabilityRegistry as h, type CapabilityServices as i, type ChatControllerRegistry as j, type ChatScope as k, type EntitlementStore as l, type EverfurRuntimeConfig as m, type RuntimeUser as n };