@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
@@ -1,1941 +1 @@
1
- 'use strict';
2
-
3
- var react = require('react');
4
- require('react-native-safe-area-context');
5
- var reactNative = require('react-native');
6
- var jsxRuntime = require('react/jsx-runtime');
7
-
8
- // src/rn/video/useVideo.ts
9
- var CONTRACT_RETRYABILITY = Object.freeze([
10
- Object.freeze({ wireCode: "AccessDeniedException", envelope: "http_envelope", httpStatus: 403, retryable: false }),
11
- Object.freeze({ wireCode: "AuthorizationException", envelope: "http_envelope", httpStatus: 401, retryable: false }),
12
- Object.freeze({ wireCode: "ConflictException", envelope: "http_envelope", httpStatus: 409, retryable: true }),
13
- Object.freeze({ wireCode: "DAILY_QUOTA_EXCEEDED", envelope: "http_envelope", httpStatus: 429, retryable: true }),
14
- Object.freeze({ wireCode: "IDEMPOTENCY_KEY_IN_FLIGHT", envelope: "http_envelope", httpStatus: 409, retryable: true }),
15
- Object.freeze({ wireCode: "IDEMPOTENCY_KEY_REUSED_DIFFERENT_REQUEST", envelope: "http_envelope", httpStatus: 409, retryable: false }),
16
- Object.freeze({ wireCode: "InternalFailureException", envelope: "http_envelope", httpStatus: 500, retryable: true }),
17
- Object.freeze({ wireCode: "NotImplementedException", envelope: "http_envelope", httpStatus: 501, retryable: false }),
18
- Object.freeze({ wireCode: "PaymentRequiredException", envelope: "http_envelope", httpStatus: 402, retryable: false }),
19
- Object.freeze({ wireCode: "ResourceNotFoundException", envelope: "http_envelope", httpStatus: 404, retryable: false }),
20
- Object.freeze({ wireCode: "ServiceUnavailableException", envelope: "http_envelope", httpStatus: 503, retryable: true }),
21
- Object.freeze({ wireCode: "TIER_UPGRADE_REQUIRED", envelope: "http_envelope", httpStatus: 402, retryable: false }),
22
- Object.freeze({ wireCode: "ThrottlingException", envelope: "http_envelope", httpStatus: 429, retryable: true }),
23
- Object.freeze({ wireCode: "ValidationException", envelope: "http_envelope", httpStatus: 400, retryable: false }),
24
- Object.freeze({ wireCode: "accessDenied", envelope: "http_envelope", httpStatus: 403, retryable: false }),
25
- Object.freeze({ wireCode: "authExpired", envelope: "http_envelope", httpStatus: 401, retryable: true }),
26
- Object.freeze({ wireCode: "internal_error", envelope: "sse_frame", httpStatus: 200, retryable: false }),
27
- Object.freeze({ wireCode: "invalidRequest", envelope: "http_envelope", httpStatus: 400, retryable: false }),
28
- Object.freeze({ wireCode: "not_found", envelope: "sse_frame", httpStatus: 200, retryable: false }),
29
- Object.freeze({ wireCode: "session_required", envelope: "http_envelope", httpStatus: 403, retryable: false }),
30
- Object.freeze({ wireCode: "timeout", envelope: "sse_frame", httpStatus: 200, retryable: true }),
31
- Object.freeze({ wireCode: "unavailable", envelope: "http_envelope", httpStatus: 503, retryable: true })
32
- ]);
33
- Object.freeze(
34
- CONTRACT_RETRYABILITY.map((row) => row.wireCode)
35
- );
36
-
37
- // src/client/errors/contract.ts
38
- var NORMALIZED_FOLD = Object.freeze({
39
- // the default status-mapped envelope
40
- ValidationException: "validationFailed",
41
- AuthorizationException: "authRejected",
42
- PaymentRequiredException: "paymentRequired",
43
- AccessDeniedException: "accessDenied",
44
- ResourceNotFoundException: "resourceNotFound",
45
- ConflictException: "conflict",
46
- ThrottlingException: "rateLimited",
47
- InternalFailureException: "internalError",
48
- NotImplementedException: "notImplemented",
49
- ServiceUnavailableException: "serviceUnavailable",
50
- // dict-detail codes on the widget surface
51
- authExpired: "authExpired",
52
- unavailable: "serviceUnavailable",
53
- session_required: "sessionRequired",
54
- accessDenied: "accessDenied",
55
- invalidRequest: "sdkUpdateRequired",
56
- // typed codes
57
- IDEMPOTENCY_KEY_IN_FLIGHT: "idempotencyInFlight",
58
- IDEMPOTENCY_KEY_REUSED_DIFFERENT_REQUEST: "idempotencyConflict",
59
- DAILY_QUOTA_EXCEEDED: "quotaExceeded",
60
- TIER_UPGRADE_REQUIRED: "paymentRequired",
61
- // SSE error frames
62
- not_found: "conversationNotFound",
63
- timeout: "streamTimeout",
64
- internal_error: "internalError"
65
- });
66
- var CONTRACT_ERROR_ROWS = Object.freeze(
67
- CONTRACT_RETRYABILITY.map(
68
- (generated) => Object.freeze({
69
- wireCode: generated.wireCode,
70
- envelope: generated.envelope,
71
- retryable: generated.retryable,
72
- normalized: NORMALIZED_FOLD[generated.wireCode]
73
- })
74
- )
75
- );
76
- var BY_UPPER = new Map(
77
- CONTRACT_ERROR_ROWS.map((r) => [r.wireCode.toUpperCase(), r])
78
- );
79
- function contractErrorFor(wireCode) {
80
- if (typeof wireCode !== "string") return void 0;
81
- const key = wireCode.trim().toUpperCase();
82
- return key.length > 0 ? BY_UPPER.get(key) : void 0;
83
- }
84
- var CATEGORY_OF = Object.freeze({
85
- // the caller's sign-in state is wrong and a credential change is the remedy
86
- authExpired: "authentication",
87
- authRejected: "authentication",
88
- sessionRequired: "authentication",
89
- // signed in, correctly identified, and still not allowed
90
- accessDenied: "authorization",
91
- // a plan gate, not a permission failure: the remedy is commercial
92
- paymentRequired: "entitlement",
93
- // slow down / come back later
94
- quotaExceeded: "rateLimit",
95
- rateLimited: "rateLimit",
96
- // the request itself was wrong; retrying it unchanged can never succeed
97
- validationFailed: "invalidRequest",
98
- sdkUpdateRequired: "invalidRequest",
99
- // the target is gone, hidden, or was never visible to this caller
100
- conversationNotFound: "notFound",
101
- petNotFound: "notFound",
102
- resourceNotFound: "notFound",
103
- // something else already holds the thing
104
- conflict: "conflict",
105
- idempotencyConflict: "conflict",
106
- idempotencyInFlight: "conflict",
107
- // the pipe, not the request: timeouts, drops, a briefly unreachable dependency
108
- streamTimeout: "connection",
109
- streamIncomplete: "connection",
110
- upstreamLlmTimeout: "connection",
111
- serviceUnavailable: "connection",
112
- // the server admits fault
113
- internalError: "server",
114
- notImplemented: "server",
115
- mediaUploadFailed: "media",
116
- moderationBlocked: "policy",
117
- unknown: "unknown"
118
- });
119
- function categoryFor(code) {
120
- return CATEGORY_OF[code];
121
- }
122
- function fromWire(wire) {
123
- const contractRow = contractErrorFor(wire);
124
- if (contractRow) return contractRow.normalized;
125
- const upper = (wire ?? "").trim().toUpperCase();
126
- const canonical = upper.replace(/[^A-Z0-9]/g, "");
127
- if (canonical === "AUTHREJECTED") return "authRejected";
128
- if (upper.startsWith("AUTH")) return "authExpired";
129
- switch (canonical) {
130
- case "DAILYQUOTAEXCEEDED":
131
- case "QUOTAEXCEEDED":
132
- return "quotaExceeded";
133
- case "RATELIMITED":
134
- case "TOOMANYREQUESTS":
135
- return "rateLimited";
136
- case "IDEMPOTENCYKEYCONFLICT":
137
- case "IDEMPOTENCYCONFLICT":
138
- return "idempotencyConflict";
139
- case "ACCESSDENIED":
140
- case "FORBIDDEN":
141
- return "accessDenied";
142
- case "NOTFOUND":
143
- case "CONVERSATIONNOTFOUND":
144
- return "conversationNotFound";
145
- case "PETNOTFOUND":
146
- return "petNotFound";
147
- case "TIMEOUT":
148
- case "STREAMTIMEOUT":
149
- return "streamTimeout";
150
- // Analysis-domain deterministic 400s (NON-retryable). Additive per SPEC-00 §3.1 / SPEC-11b §2.6:
151
- // fold to a NON-retried target so the SDK never retries a permanently-failing bad-input request.
152
- // (Comment above the group so the empty fall-through cases stay lint-clean under no-fallthrough.)
153
- case "MEDIAUPLOADFAILED":
154
- case "UNRECOGNIZEDVIDEOFORMAT":
155
- case "FILETOOSMALL":
156
- return "mediaUploadFailed";
157
- case "MODERATIONBLOCKED":
158
- return "moderationBlocked";
159
- case "UPSTREAMLLMTIMEOUT":
160
- return "upstreamLlmTimeout";
161
- case "INTERNALERROR":
162
- case "INTERNALSERVERERROR":
163
- return "internalError";
164
- case "STREAMINCOMPLETE":
165
- case "CHATSTREAMINCOMPLETE":
166
- return "streamIncomplete";
167
- // Deterministic 400s that can only come from an SDK bug -> unknown (NON-retryable). NEVER internalError
168
- // (which IS auto-retried): mapping a bad-input 400 to a retryable code retries a request that never succeeds.
169
- case "INVALIDANALYSISTYPE":
170
- case "INVALIDUPLOADPURPOSE":
171
- return "unknown";
172
- default:
173
- return "unknown";
174
- }
175
- }
176
- var AUTO_RETRYABLE_CODES = /* @__PURE__ */ new Set([
177
- "streamTimeout",
178
- "rateLimited",
179
- "internalError",
180
- "upstreamLlmTimeout",
181
- "streamIncomplete"
182
- ]);
183
- function isAutoRetryable(code) {
184
- return AUTO_RETRYABLE_CODES.has(code);
185
- }
186
- var RETRYABLE_CONTRACT_CODES = /* @__PURE__ */ new Set([
187
- "serviceUnavailable",
188
- "conflict",
189
- "idempotencyInFlight"
190
- ]);
191
- function isRetryableCode(code) {
192
- return isAutoRetryable(code) || RETRYABLE_CONTRACT_CODES.has(code);
193
- }
194
- var BACKOFF_BASE_MS = 500;
195
- var BACKOFF_CAP_MS = 8e3;
196
- var MAX_RETRY_ATTEMPTS = 4;
197
- function backoffDelayMs(attempt, rng = Math.random) {
198
- const bounded = Math.max(0, attempt);
199
- const ceiling = Math.min(BACKOFF_BASE_MS * 2 ** bounded, BACKOFF_CAP_MS);
200
- return Math.floor(rng() * ceiling);
201
- }
202
-
203
- // src/client/errors/EverfurError.ts
204
- var USER_FACING_COPY = Object.freeze({
205
- authExpired: "Please sign in again to continue.",
206
- authRejected: "Please sign in again to continue.",
207
- sessionRequired: "This app needs to be updated to keep using Everfur.",
208
- accessDenied: "This feature isn't available on your account right now.",
209
- paymentRequired: "This is part of a paid plan.",
210
- quotaExceeded: "You have used today's messages. This resets tomorrow.",
211
- rateLimited: "You are sending messages quickly. Try again in a few seconds.",
212
- validationFailed: "Something in that request was not right. Check the details and try again.",
213
- sdkUpdateRequired: "This app needs to be updated to keep using Everfur.",
214
- conflict: "That is already running. Wait for it to finish, then try again.",
215
- idempotencyConflict: "That message was already delivered.",
216
- idempotencyInFlight: "That is still processing. Give it a moment.",
217
- conversationNotFound: "This conversation is no longer available.",
218
- petNotFound: "We could not find that pet. Please pick a pet and try again.",
219
- resourceNotFound: "We could not find that.",
220
- streamTimeout: "The response timed out. Tap retry to try again.",
221
- mediaUploadFailed: "We could not upload that photo. Please attach it again.",
222
- moderationBlocked: "This request cannot be answered here.",
223
- upstreamLlmTimeout: "The response timed out. Tap retry to try again.",
224
- internalError: "We hit a snag on our end. Please try again.",
225
- notImplemented: "That is not available yet.",
226
- serviceUnavailable: "That service is briefly unavailable. Try again shortly.",
227
- streamIncomplete: "The connection dropped mid-answer. Tap retry to continue.",
228
- unknown: "Something went wrong. Please try again."
229
- });
230
- function userFacingError(code) {
231
- return USER_FACING_COPY[code];
232
- }
233
- function defineHidden(target, key, value) {
234
- Object.defineProperty(target, key, {
235
- value,
236
- enumerable: false,
237
- writable: false,
238
- configurable: false
239
- });
240
- }
241
- function makeEverfurError(code, opts) {
242
- contractErrorFor(opts?.wireCode);
243
- const error = {
244
- code,
245
- category: categoryFor(code),
246
- retryable: opts?.retryable ?? isRetryableCode(code),
247
- displayMessage: userFacingError(code)
248
- };
249
- if (opts?.httpStatus !== void 0) error.httpStatus = opts.httpStatus;
250
- if (opts?.requestId) error.requestId = opts.requestId;
251
- if (opts?.wireMessage) defineHidden(error, "wireMessage", opts.wireMessage);
252
- if (opts?.raw !== void 0 && opts.raw !== null) defineHidden(error, "raw", opts.raw);
253
- return Object.freeze(error);
254
- }
255
-
256
- // src/client/errors/EverfurResult.ts
257
- function ok(value, warning) {
258
- return Object.freeze(
259
- { ok: true, value }
260
- );
261
- }
262
- function err(error) {
263
- return Object.freeze({ ok: false, error });
264
- }
265
- var EverfurConfigError = class _EverfurConfigError extends Error {
266
- constructor(kind, message) {
267
- super(message);
268
- this.name = "EverfurConfigError";
269
- this.kind = kind;
270
- Object.setPrototypeOf(this, _EverfurConfigError.prototype);
271
- }
272
- };
273
-
274
- // src/client/protocol/frames.ts
275
- function assertNever(x) {
276
- throw new Error(
277
- `assertNever: unhandled discriminated-union member ${JSON.stringify(x)}`
278
- );
279
- }
280
- function errorFrame(fields) {
281
- return Object.freeze({
282
- kind: "error",
283
- terminal: true,
284
- code: fields.code,
285
- retryable: fields.retryable,
286
- wireMessage: fields.wireMessage,
287
- synthesized: fields.synthesized,
288
- lastEventId: fields.lastEventId,
289
- serverRequestId: null
290
- });
291
- }
292
-
293
- // src/client/identity.ts
294
- function authHeaders(auth) {
295
- const headers = {};
296
- if (auth.mode === "personalized") {
297
- headers.authorization = `Bearer ${auth.token}`;
298
- } else {
299
- headers["x-everfur-partner-key"] = auth.publishableKey;
300
- }
301
- if (auth.userRef) headers["x-everfur-user-ref"] = auth.userRef;
302
- if (auth.sessionId) headers["x-everfur-session-id"] = auth.sessionId;
303
- return headers;
304
- }
305
- function withAuthHeaders(req, auth) {
306
- return { ...req, headers: { ...stripAuthHeaders(req.headers), ...authHeaders(auth) } };
307
- }
308
- function stripAuthHeaders(headers) {
309
- if (!headers) return {};
310
- const out = {};
311
- for (const [key, value] of Object.entries(headers)) {
312
- const lower = key.toLowerCase();
313
- if (lower === "authorization" || lower === "x-everfur-partner-key") continue;
314
- out[key] = value;
315
- }
316
- return out;
317
- }
318
- async function authedSend(req, ctx) {
319
- if (ctx.mode === "anonymous") {
320
- return ctx.transport.request(
321
- withAuthHeaders(req, {
322
- mode: "anonymous",
323
- publishableKey: ctx.publishableKey,
324
- userRef: ctx.userRef,
325
- ...ctx.sessionId ? { sessionId: ctx.sessionId } : {}
326
- })
327
- );
328
- }
329
- let token;
330
- try {
331
- token = await ctx.identity.currentToken();
332
- } catch {
333
- return err(authRejectedError());
334
- }
335
- const first = await ctx.transport.request(
336
- withAuthHeaders(req, {
337
- mode: "personalized",
338
- token,
339
- userRef: ctx.userRef,
340
- ...ctx.sessionId ? { sessionId: ctx.sessionId } : {}
341
- })
342
- );
343
- if (first.ok || first.error.httpStatus !== 401) return first;
344
- let refreshed;
345
- try {
346
- refreshed = await ctx.identity.currentToken({ forceRefresh: true });
347
- } catch {
348
- return err(authRejectedError());
349
- }
350
- const retry = await ctx.transport.request(
351
- withAuthHeaders(req, {
352
- mode: "personalized",
353
- token: refreshed,
354
- userRef: ctx.userRef,
355
- ...ctx.sessionId ? { sessionId: ctx.sessionId } : {}
356
- })
357
- );
358
- if (!retry.ok && retry.error.httpStatus === 401) {
359
- return err(authRejectedError(retry.error.httpStatus));
360
- }
361
- return retry;
362
- }
363
- function authRejectedError(httpStatus) {
364
- return makeEverfurError("authRejected", {
365
- retryable: false,
366
- ...httpStatus !== void 0 ? { httpStatus } : {}
367
- });
368
- }
369
-
370
- // src/client/transport.ts
371
- function networkError(cause) {
372
- return makeEverfurError("internalError", { retryable: true, wireMessage: describeCause(cause) });
373
- }
374
- function describeCause(cause) {
375
- if (cause instanceof Error) return cause.message;
376
- return typeof cause === "string" ? cause : "network request failed";
377
- }
378
-
379
- // src/client/funnel/requestFunnel.ts
380
- var FUNNEL_UNARY_TIMEOUT_MS = 3e4;
381
- var RETRY_OVERRIDE_MAX_MS = BACKOFF_CAP_MS * 2;
382
- var IDEMPOTENCY_HEADER = "idempotency-key";
383
- var MUTATING_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
384
- var NOOP_OBSERVER = Object.freeze({
385
- onEvent() {
386
- }
387
- });
388
- var defaultFunnelTimers = Object.freeze({
389
- setTimeout: (handler, ms) => setTimeout(handler, ms),
390
- clearTimeout: (handle) => clearTimeout(handle),
391
- now: () => Date.now()
392
- });
393
- function createRequestFunnel(deps) {
394
- const observer = deps.observer ?? NOOP_OBSERVER;
395
- const timers = deps.timers ?? defaultFunnelTimers;
396
- const rng = deps.rng ?? Math.random;
397
- const sleep = deps.sleep ?? ((ms, signal) => delayAbortable(ms, timers, signal));
398
- const ceiling = deps.maxAttempts ?? MAX_RETRY_ATTEMPTS;
399
- const timeoutMs = deps.unaryTimeoutMs ?? FUNNEL_UNARY_TIMEOUT_MS;
400
- const mintKey = deps.newIdempotencyKey ?? defaultIdempotencyKeyFactory();
401
- const emit = (event) => {
402
- try {
403
- observer.onEvent(event);
404
- } catch {
405
- }
406
- };
407
- async function send(call, req, opts) {
408
- const perCallMaxAttempts = opts?.maxAttempts;
409
- const maxAttempts = Math.max(1, perCallMaxAttempts ?? ceiling);
410
- const idempotent = opts?.idempotent ?? MUTATING_METHODS.has(req.method);
411
- const idempotencyKey = idempotent ? mintKey() : null;
412
- const base = assembleHeaders(req, idempotencyKey);
413
- let attempt = 0;
414
- for (; ; ) {
415
- const stamp = (extra) => Object.freeze({
416
- call,
417
- method: req.method,
418
- path: req.path,
419
- attempt,
420
- ...idempotencyKey ? { idempotencyKey } : {},
421
- ...extra
422
- });
423
- emit(stamp({ phase: "attempt" }));
424
- const deadline = boundedSignal(timeoutMs, timers);
425
- const merged = mergeSignals(deadline.signal, req.signal);
426
- let settled;
427
- try {
428
- settled = await authedSend({ ...base, signal: merged.signal }, deps.auth);
429
- } catch (cause) {
430
- settled = err(networkError(cause));
431
- } finally {
432
- merged.dispose();
433
- deadline.cancel();
434
- }
435
- if (settled.ok) {
436
- emit(stamp({ phase: "settled", ok: true, reason: "ok" }));
437
- return settled;
438
- }
439
- const error = settled.error;
440
- const decision = decideRetry(error, attempt, {
441
- maxAttempts,
442
- rng,
443
- now: timers.now(),
444
- aborted: req.signal?.aborted === true,
445
- ...perCallMaxAttempts !== void 0 ? { perCallMaxAttempts } : {},
446
- ...deps.errorPolicy !== void 0 ? { policy: deps.errorPolicy } : {}
447
- });
448
- const settledEvent = {
449
- ok: false,
450
- code: error.code,
451
- reason: decision.reason,
452
- ...error.httpStatus !== void 0 ? { httpStatus: error.httpStatus } : {},
453
- ...error.requestId !== void 0 ? { requestId: error.requestId } : {}
454
- };
455
- if (!decision.retry) {
456
- emit(stamp({ phase: "settled", ...settledEvent }));
457
- return err(error);
458
- }
459
- emit(stamp({ phase: "retry", ...settledEvent, delayMs: decision.delayMs }));
460
- await sleep(decision.delayMs, req.signal);
461
- if (req.signal?.aborted === true) {
462
- emit(stamp({ phase: "settled", ...settledEvent, reason: "aborted" }));
463
- return err(error);
464
- }
465
- attempt += 1;
466
- }
467
- }
468
- async function authorize(req) {
469
- return withAuthHeaders(req, await resolveDeviceAuth(deps.auth));
470
- }
471
- async function* stream(call, req, signal) {
472
- emit(
473
- Object.freeze({
474
- call,
475
- phase: "stream.open",
476
- method: req.method,
477
- path: req.path,
478
- attempt: 0
479
- })
480
- );
481
- try {
482
- yield* deps.auth.transport.stream(req, signal);
483
- } catch {
484
- yield errorFrame({
485
- // The same wire spelling sse.ts synthesizes, so `fromWire` folds it to the retryable `internalError`
486
- // like any other transport failure. wireMessage stays null: it is the SERVER's text, and a thrown
487
- // host exception's `.message` is not that.
488
- code: "internal_error",
489
- retryable: true,
490
- wireMessage: null,
491
- synthesized: true,
492
- lastEventId: null});
493
- }
494
- }
495
- return Object.freeze({ send, authorize, stream });
496
- }
497
- function assembleHeaders(req, idempotencyKey) {
498
- const headers = {};
499
- for (const [key, value] of Object.entries(req.headers ?? {})) {
500
- if (key.toLowerCase() === IDEMPOTENCY_HEADER) continue;
501
- headers[key] = value;
502
- }
503
- if (idempotencyKey !== null) headers["Idempotency-Key"] = idempotencyKey;
504
- return { ...req, headers };
505
- }
506
- function defaultIdempotencyKeyFactory() {
507
- let counter = 0;
508
- return () => `efk_${Date.now().toString(36)}_${(counter++).toString(36)}_${Math.random().toString(36).slice(2, 12)}`;
509
- }
510
- var POLICY_MAX_ATTEMPTS_CAP = 10;
511
- function decideRetry(error, attempt, ctx) {
512
- if (ctx.aborted) return { retry: false, reason: "aborted" };
513
- const throttle = throttleKind(error);
514
- if (throttle === "quota") {
515
- return { retry: false, reason: "daily-quota-exhausted" };
516
- }
517
- if (throttle !== "burst" && !isAutoRetryable(error.code)) {
518
- return { retry: false, reason: "terminal-code" };
519
- }
520
- const reason = throttle === "burst" ? "burst-throttle" : "retryable-code";
521
- const policyVetoedRetry = () => {
522
- if (ctx.policy === void 0) return false;
523
- try {
524
- return !ctx.policy.shouldRetry(error.code, attempt);
525
- } catch {
526
- return false;
527
- }
528
- };
529
- if (policyVetoedRetry()) return { retry: false, reason: "terminal-code" };
530
- const policyCeiling = () => {
531
- if (ctx.policy === void 0) return null;
532
- let raw;
533
- try {
534
- raw = ctx.policy.maxAttempts(error.code);
535
- } catch {
536
- return null;
537
- }
538
- if (typeof raw !== "number" || !Number.isFinite(raw)) return null;
539
- return Math.max(1, Math.min(POLICY_MAX_ATTEMPTS_CAP, Math.floor(raw)));
540
- };
541
- const ceiling = ctx.perCallMaxAttempts ?? policyCeiling() ?? ctx.maxAttempts;
542
- if (attempt + 1 >= ceiling) return { retry: false, reason: "attempts-exhausted" };
543
- const override = retryOverrideMs(error, ctx.now);
544
- if (override !== null) {
545
- if (override > RETRY_OVERRIDE_MAX_MS) {
546
- return { retry: false, reason: "server-reset-beyond-budget" };
547
- }
548
- return { retry: true, reason, delayMs: override };
549
- }
550
- return { retry: true, reason, delayMs: backoffDelayMs(attempt, ctx.rng) };
551
- }
552
- function throttleKind(error) {
553
- const code = error.code === "unknown" ? fromWire(rawWireCode(error.raw)) : error.code;
554
- if (code === "quotaExceeded") return "quota";
555
- if (code === "rateLimited") return "burst";
556
- if (error.httpStatus === 429) {
557
- return "burst";
558
- }
559
- return null;
560
- }
561
- function retryOverrideMs(error, now) {
562
- const at = findResetsAt(error.raw);
563
- if (at === null) return null;
564
- const delta = at - now;
565
- return delta > 0 ? delta : 0;
566
- }
567
- function findResetsAt(raw) {
568
- for (const scope of candidateScopes(raw)) {
569
- const value = scope["resets_at"];
570
- const parsed = parseInstantMs(value);
571
- if (parsed !== null) return parsed;
572
- }
573
- return null;
574
- }
575
- function candidateScopes(raw) {
576
- if (!isRecord(raw)) return [];
577
- const out = [raw];
578
- for (const nested of ["error", "detail"]) {
579
- const value = raw[nested];
580
- if (isRecord(value)) out.push(value);
581
- }
582
- return out;
583
- }
584
- function parseInstantMs(value) {
585
- if (typeof value === "number" && Number.isFinite(value)) {
586
- return value < 1e11 ? Math.round(value * 1e3) : Math.round(value);
587
- }
588
- if (typeof value === "string" && value.trim().length > 0) {
589
- const parsed = Date.parse(value);
590
- return Number.isFinite(parsed) ? parsed : null;
591
- }
592
- return null;
593
- }
594
- function rawWireCode(raw) {
595
- for (const scope of candidateScopes(raw)) {
596
- const code = scope["code"];
597
- if (typeof code === "string" && code.length > 0) return code;
598
- }
599
- return null;
600
- }
601
- function isRecord(value) {
602
- return typeof value === "object" && value !== null;
603
- }
604
- async function resolveDeviceAuth(auth) {
605
- if (auth.mode === "anonymous") {
606
- return {
607
- mode: "anonymous",
608
- publishableKey: auth.publishableKey,
609
- userRef: auth.userRef,
610
- ...auth.sessionId ? { sessionId: auth.sessionId } : {}
611
- };
612
- }
613
- return {
614
- mode: "personalized",
615
- token: await auth.identity.currentToken(),
616
- userRef: auth.userRef,
617
- ...auth.sessionId ? { sessionId: auth.sessionId } : {}
618
- };
619
- }
620
- function boundedSignal(ms, timers) {
621
- const ctrl = new AbortController();
622
- const handle = timers.setTimeout(() => ctrl.abort(), ms);
623
- return { signal: ctrl.signal, cancel: () => timers.clearTimeout(handle) };
624
- }
625
- function mergeSignals(deadline, caller) {
626
- const noop = () => void 0;
627
- if (!caller) return { signal: deadline, dispose: noop };
628
- const ctrl = new AbortController();
629
- if (deadline.aborted || caller.aborted) {
630
- ctrl.abort();
631
- return { signal: ctrl.signal, dispose: noop };
632
- }
633
- const onAbort = () => ctrl.abort();
634
- deadline.addEventListener("abort", onAbort, { once: true });
635
- caller.addEventListener("abort", onAbort, { once: true });
636
- return {
637
- signal: ctrl.signal,
638
- dispose: () => {
639
- deadline.removeEventListener("abort", onAbort);
640
- caller.removeEventListener("abort", onAbort);
641
- }
642
- };
643
- }
644
- function delayAbortable(ms, timers, signal) {
645
- if (ms <= 0 || signal?.aborted === true) return Promise.resolve();
646
- return new Promise((resolve) => {
647
- const handle = timers.setTimeout(() => {
648
- signal?.removeEventListener("abort", onAbort);
649
- resolve();
650
- }, ms);
651
- const onAbort = () => {
652
- timers.clearTimeout(handle);
653
- resolve();
654
- };
655
- signal?.addEventListener("abort", onAbort, { once: true });
656
- });
657
- }
658
-
659
- // src/core/ports/TelemetryPort.ts
660
- var noopTelemetry = Object.freeze({
661
- track() {
662
- },
663
- trackExpectedFailure() {
664
- }
665
- });
666
- function fileHandleFromMedia(source) {
667
- const handle = {
668
- uri: source.uri,
669
- mimeType: source.mimeType
670
- };
671
- const size = mediaByteSize(source);
672
- if (size !== null) handle.sizeBytes = size;
673
- return Object.freeze(handle);
674
- }
675
- function mediaByteSize(source) {
676
- const candidate = source.byteSize ?? source.sizeBytes;
677
- return typeof candidate === "number" && Number.isFinite(candidate) && candidate >= 0 ? candidate : null;
678
- }
679
- function mediaDurationMs(source) {
680
- const seconds = source.durationSeconds;
681
- return typeof seconds === "number" && Number.isFinite(seconds) && seconds >= 0 ? Math.round(seconds * 1e3) : null;
682
- }
683
- function captureOptionsFor(mode, maxDurationSeconds) {
684
- return maxDurationSeconds === void 0 ? Object.freeze({ mode }) : Object.freeze({ mode, maxDurationSeconds });
685
- }
686
- function isCaptureAvailable(camera) {
687
- return camera != null && camera.isAvailable === true;
688
- }
689
- var defaultTimers = Object.freeze({
690
- setTimeout: (handler, ms) => setTimeout(handler, ms),
691
- clearTimeout: (handle) => clearTimeout(handle),
692
- now: () => Date.now()
693
- });
694
- function delayAbortable2(ms, signal, timers) {
695
- return new Promise((resolve) => {
696
- if (signal.aborted) {
697
- resolve();
698
- return;
699
- }
700
- const holder = { handle: void 0 };
701
- const done = () => {
702
- timers.clearTimeout(holder.handle);
703
- signal.removeEventListener("abort", done);
704
- resolve();
705
- };
706
- holder.handle = timers.setTimeout(done, ms);
707
- signal.addEventListener("abort", done, { once: true });
708
- });
709
- }
710
- function mergeSignals2(a, b) {
711
- if (!b) return a;
712
- const ctrl = new AbortController();
713
- if (a.aborted || b.aborted) {
714
- ctrl.abort();
715
- return ctrl.signal;
716
- }
717
- const onAbort = () => ctrl.abort();
718
- a.addEventListener("abort", onAbort, { once: true });
719
- b.addEventListener("abort", onAbort, { once: true });
720
- return ctrl.signal;
721
- }
722
- var MediaStore = class {
723
- constructor(initial) {
724
- this.listeners = /* @__PURE__ */ new Set();
725
- this.epoch = 0;
726
- this.inFlight = null;
727
- this.snapshot = Object.freeze({ ...initial });
728
- }
729
- subscribe(listener) {
730
- this.listeners.add(listener);
731
- return () => {
732
- this.listeners.delete(listener);
733
- };
734
- }
735
- getSnapshot() {
736
- return this.snapshot;
737
- }
738
- patch(partial) {
739
- this.snapshot = Object.freeze({ ...this.snapshot, ...partial });
740
- for (const listener of this.listeners) listener();
741
- }
742
- beginOperation(external) {
743
- this.inFlight?.abort();
744
- const ctrl = new AbortController();
745
- this.inFlight = ctrl;
746
- const epoch = this.epoch;
747
- const signal = external ? mergeSignals2(ctrl.signal, external) : ctrl.signal;
748
- return {
749
- signal,
750
- epoch,
751
- settle: () => {
752
- if (this.inFlight === ctrl) this.inFlight = null;
753
- }
754
- };
755
- }
756
- alive(epoch) {
757
- return epoch === this.epoch;
758
- }
759
- /** Re-scope on user/pet switch: aborts in-flight and bumps the epoch so a late write drops. */
760
- rescope() {
761
- this.epoch += 1;
762
- this.inFlight?.abort();
763
- this.inFlight = null;
764
- }
765
- /** Idempotent teardown: aborts in-flight, bumps the epoch, drops every listener. */
766
- dispose() {
767
- this.epoch += 1;
768
- this.inFlight?.abort();
769
- this.inFlight = null;
770
- this.listeners.clear();
771
- }
772
- };
773
-
774
- // src/client/text.ts
775
- function hasLoneSurrogate(value) {
776
- for (let i = 0; i < value.length; i += 1) {
777
- const code = value.charCodeAt(i);
778
- if (code >= 55296 && code <= 56319) {
779
- const low = value.charCodeAt(i + 1);
780
- if (!(low >= 56320 && low <= 57343)) return true;
781
- i += 1;
782
- } else if (code >= 56320 && code <= 57343) {
783
- return true;
784
- }
785
- }
786
- return false;
787
- }
788
-
789
- // src/core/media/projection.ts
790
- var CONSUMER_SAFE_DETECTION_KEYS = Object.freeze([
791
- "primary_label",
792
- "primary_confidence",
793
- "subtype_label",
794
- "subtype_display",
795
- "subtype_confidence",
796
- "risk_tier",
797
- "confidence_tier",
798
- "copy_short",
799
- "disease_name",
800
- "disease_framing",
801
- "model_version",
802
- "skin_v2",
803
- "eye_v2",
804
- "llm_checkup"
805
- ]);
806
- var OPERATOR_INTERNAL_KEYS = Object.freeze([
807
- "decision_audit",
808
- "decision_thresholds",
809
- "per_class_auroc",
810
- "ood_layers",
811
- "gate1_score",
812
- "possible_conditions",
813
- "top_class"
814
- ]);
815
- var BOOLEAN_KEYS = /* @__PURE__ */ new Set(["skin_v2", "eye_v2", "llm_checkup"]);
816
- var CONFIDENCE_KEYS = /* @__PURE__ */ new Set(["primary_confidence", "subtype_confidence"]);
817
- var RISK_TIER_VALUES = /* @__PURE__ */ new Set(["low_risk", "concern", "uncertain"]);
818
- var CONFIDENCE_TIER_VALUES = /* @__PURE__ */ new Set(["possible", "probable"]);
819
- var STRING_LIMITS = Object.freeze({
820
- primary_label: 64,
821
- subtype_label: 64,
822
- subtype_display: 128,
823
- copy_short: 512,
824
- disease_name: 128,
825
- disease_framing: 512,
826
- model_version: 64
827
- });
828
- function projectDetectionMetadata(raw) {
829
- const source = isRecord2(raw) ? raw : {};
830
- const out = {};
831
- for (const key of CONSUMER_SAFE_DETECTION_KEYS) {
832
- if (!(key in source)) continue;
833
- const value = source[key];
834
- const accepted = acceptKey(key, value);
835
- if (accepted !== DROP) out[key] = accepted;
836
- }
837
- let dropped = 0;
838
- for (const key of OPERATOR_INTERNAL_KEYS) {
839
- if (key in source) dropped += 1;
840
- }
841
- return Object.freeze({ metadata: Object.freeze(out), droppedOperatorKeys: dropped });
842
- }
843
- var DROP = /* @__PURE__ */ Symbol("drop");
844
- function acceptKey(key, value) {
845
- if (BOOLEAN_KEYS.has(key)) return typeof value === "boolean" ? value : DROP;
846
- if (CONFIDENCE_KEYS.has(key)) return isUnitConfidence(value) ? value : DROP;
847
- if (key === "risk_tier") return typeof value === "string" && RISK_TIER_VALUES.has(value) ? value : DROP;
848
- if (key === "confidence_tier") {
849
- return typeof value === "string" && CONFIDENCE_TIER_VALUES.has(value) ? value : DROP;
850
- }
851
- if (typeof value !== "string") return DROP;
852
- const limit = STRING_LIMITS[key] ?? 256;
853
- return value.length > limit ? value.slice(0, limit) : value;
854
- }
855
- function isUnitConfidence(value) {
856
- return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
857
- }
858
- function parseInterpretation(raw) {
859
- if (!isRecord2(raw)) return null;
860
- const summary = asString(raw["summary"]);
861
- const disclaimer = asString(raw["disclaimer"]);
862
- const recommendations = asStringList(raw["recommendations"] ?? raw["recommendation"]);
863
- if (summary === null && disclaimer === null && recommendations.length === 0) return null;
864
- return Object.freeze({
865
- summary: summary ?? "",
866
- recommendations: Object.freeze(recommendations),
867
- disclaimer: disclaimer ?? ""
868
- });
869
- }
870
- function locateDetectionMetadata(body) {
871
- if (!isRecord2(body)) return {};
872
- if (isRecord2(body["metadata"])) return body["metadata"];
873
- const detections = body["detections"];
874
- if (Array.isArray(detections) && detections.length > 0) {
875
- const first = detections[0];
876
- if (isRecord2(first) && isRecord2(first["metadata"])) return first["metadata"];
877
- if (isRecord2(first)) return first;
878
- }
879
- return body;
880
- }
881
- function locateInterpretation(body) {
882
- if (!isRecord2(body)) return null;
883
- if (isRecord2(body["interpretation"])) return body["interpretation"];
884
- return body;
885
- }
886
- function isRecord2(value) {
887
- return typeof value === "object" && value !== null && !Array.isArray(value);
888
- }
889
- function asString(value) {
890
- return typeof value === "string" && value.length > 0 ? value : null;
891
- }
892
- function asStringList(value) {
893
- if (typeof value === "string") return value.length > 0 ? [value] : [];
894
- if (!Array.isArray(value)) return [];
895
- return value.filter((entry) => typeof entry === "string" && entry.length > 0);
896
- }
897
- function asUnitNumber(value) {
898
- return isUnitConfidence(value) ? value : null;
899
- }
900
- function asFiniteNumber(value) {
901
- return typeof value === "number" && Number.isFinite(value) ? value : null;
902
- }
903
-
904
- // src/core/media/pipeline.ts
905
- var ENDPOINTS = Object.freeze({
906
- initiate: "/widget/v1/uploads/initiate",
907
- precheck: "/widget/v1/photo/precheck",
908
- photoAnalyze: "/widget/v1/photo/analyze",
909
- videoAnalyze: "/widget/v1/video/analyze",
910
- // `jobId` arrives from the WIRE, so it is not under this SDK's control, and `encodeURIComponent` throws
911
- // URIError on a lone surrogate. Returning null lets the caller settle instead of throwing out of the
912
- // poller. These routes are dark today, which is exactly why the guard goes in now: it costs one line and
913
- // the gap re-opens silently the moment they land.
914
- job: (jobId) => hasLoneSurrogate(jobId) ? null : `/widget/v1/analysis/jobs/${encodeURIComponent(jobId)}`
915
- });
916
- function brandJobId(raw) {
917
- return raw;
918
- }
919
- async function initiateUpload(funnel, params) {
920
- const body = { purpose: params.purpose, content_type: params.contentType };
921
- if (params.byteSize !== null) body.byte_size = params.byteSize;
922
- const req = {
923
- method: "POST",
924
- path: ENDPOINTS.initiate,
925
- body,
926
- ...params.signal ? { signal: params.signal } : {}
927
- };
928
- const res = await funnel.send("media.initiateUpload", req);
929
- if (!res.ok) return err(res.error);
930
- const policy = parsePresigned(res.value);
931
- if (policy === null) return err(makeEverfurError("mediaUploadFailed", { retryable: false }));
932
- return ok(policy);
933
- }
934
- function parsePresigned(raw) {
935
- if (!isRecord2(raw)) return null;
936
- const url = asString(raw["url"]);
937
- const s3Key = asString(raw["s3_key"]);
938
- if (url === null || s3Key === null) return null;
939
- const fieldsRaw = raw["fields"];
940
- const fields = {};
941
- if (isRecord2(fieldsRaw)) {
942
- for (const [key, value] of Object.entries(fieldsRaw)) {
943
- if (typeof value === "string") fields[key] = value;
944
- }
945
- }
946
- return Object.freeze({
947
- url,
948
- fields: Object.freeze(fields),
949
- s3Key,
950
- expiresAt: asFiniteNumber(raw["expires_at"])
951
- });
952
- }
953
- async function stageUpload(upload, source, policy, idempotencyKey, signal) {
954
- try {
955
- await upload.presignedPut(fileHandleFromMedia(source), {
956
- target: policy,
957
- idempotencyKey,
958
- ...signal ? { signal } : {}
959
- });
960
- return ok(void 0);
961
- } catch {
962
- return err(makeEverfurError("mediaUploadFailed", { retryable: false }));
963
- }
964
- }
965
- async function submitAnalyze(funnel, path, body, detectGate, signal) {
966
- const req = {
967
- method: "POST",
968
- path,
969
- body,
970
- ...signal ? { signal } : {}
971
- };
972
- const res = await funnel.send("media.analyze", req);
973
- if (res.ok) {
974
- const job = parseAsyncJob(res.value);
975
- return job !== null ? { kind: "async", job } : { kind: "sync", body: res.value };
976
- }
977
- const gate = detectGate(res.error.httpStatus, res.error);
978
- if (gate !== null) return { kind: "gate", gate };
979
- return { kind: "error", error: res.error };
980
- }
981
- function parseAsyncJob(raw) {
982
- if (!isRecord2(raw)) return null;
983
- const jobId = asString(raw["job_id"]);
984
- if (jobId === null) return null;
985
- const status = asString(raw["status"]);
986
- if (status !== null && isTerminalStatus(status)) return null;
987
- return Object.freeze({
988
- jobId: brandJobId(jobId),
989
- estimatedSeconds: asFiniteNumber(raw["estimated_completion_seconds"])
990
- });
991
- }
992
- var POLL_FIRST_DELAY_MS = 2e3;
993
- var POLL_MAX_DELAY_MS = 5e3;
994
- var POLL_BUDGET_MS = 9e4;
995
- async function pollJob(funnel, jobId, deps) {
996
- const { timers, signal, alive } = deps;
997
- const start = timers.now();
998
- const budget = Math.max(POLL_BUDGET_MS, (deps.estimatedSeconds ?? 0) * 1e3);
999
- let delay = POLL_FIRST_DELAY_MS;
1000
- for (; ; ) {
1001
- if (signal.aborted || !alive()) return { kind: "aborted" };
1002
- await delayAbortable2(delay, signal, timers);
1003
- if (signal.aborted || !alive()) return { kind: "aborted" };
1004
- if (timers.now() - start > budget) return { kind: "timeout" };
1005
- const path = ENDPOINTS.job(jobId);
1006
- if (path === null) return { kind: "notFound" };
1007
- const res = await funnel.send(
1008
- "media.pollJob",
1009
- { method: "GET", path, ...signal ? { signal } : {} },
1010
- { maxAttempts: 1 }
1011
- );
1012
- if (signal.aborted || !alive()) return { kind: "aborted" };
1013
- if (!res.ok) {
1014
- if (res.error.httpStatus === 404) return { kind: "notFound" };
1015
- delay = Math.min(POLL_MAX_DELAY_MS, delay + 1e3);
1016
- continue;
1017
- }
1018
- const status = jobStatusOf(res.value);
1019
- if (status === "COMPLETED") return { kind: "complete", body: res.value };
1020
- if (status === "FAILED") return { kind: "failed", error: jobFailureError(res.value) };
1021
- delay = Math.min(POLL_MAX_DELAY_MS, delay + 1e3);
1022
- }
1023
- }
1024
- function jobStatusOf(body) {
1025
- if (!isRecord2(body)) return "PROCESSING";
1026
- return (asString(body["status"]) ?? "PROCESSING").toUpperCase();
1027
- }
1028
- function isTerminalStatus(status) {
1029
- const upper = status.toUpperCase();
1030
- return upper === "COMPLETED" || upper === "FAILED";
1031
- }
1032
- function jobFailureError(body) {
1033
- const wire = isRecord2(body) ? asString(body["error"]) ?? asString(body["error_code"]) : null;
1034
- return makeEverfurError("internalError", { retryable: false, ...wire ? { wireMessage: wire } : {} });
1035
- }
1036
- function detectNoGate() {
1037
- return null;
1038
- }
1039
- function analyzeBody(s3Key, extra) {
1040
- const body = { s3_key: s3Key };
1041
- if (extra.type !== void 0) body.type = extra.type;
1042
- if (extra.petRef != null) body.pet_id = extra.petRef;
1043
- if (extra.precheckToken != null) body.precheck_token = extra.precheckToken;
1044
- return body;
1045
- }
1046
-
1047
- // src/core/video/VideoController.ts
1048
- function createVideoController(deps) {
1049
- return new VideoController(deps);
1050
- }
1051
- var VideoController = class extends MediaStore {
1052
- constructor(deps) {
1053
- super({
1054
- status: "idle",
1055
- job: null,
1056
- result: null,
1057
- error: null,
1058
- isCaptureAvailable: isCaptureAvailable(deps.camera),
1059
- progress: 0
1060
- });
1061
- this.funnel = deps.funnel ?? createRequestFunnel({
1062
- auth: deps.auth,
1063
- ...deps.errorPolicy ? { errorPolicy: deps.errorPolicy } : {}
1064
- });
1065
- this.camera = deps.camera;
1066
- this.upload = deps.upload;
1067
- this.defaultPetRef = deps.petRef ?? null;
1068
- this.telemetry = deps.telemetry ?? noopTelemetry;
1069
- this.timers = deps.timers ?? defaultTimers;
1070
- this.makeIdempotencyKey = deps.makeIdempotencyKey ?? defaultIdempotencyFactory();
1071
- }
1072
- /** Open the CameraPort (video), then submit. No-op when capture is unavailable; a cancel returns to idle. */
1073
- async capture(opts) {
1074
- if (!isCaptureAvailable(this.camera)) {
1075
- return err(makeEverfurError("unknown", { retryable: false }));
1076
- }
1077
- let source;
1078
- try {
1079
- source = await this.camera.capture(captureOptionsFor("video", 15));
1080
- } catch {
1081
- if (this.snapshot.status !== "idle") this.patch({ status: "idle", progress: 0 });
1082
- return err(makeEverfurError("unknown", { retryable: false }));
1083
- }
1084
- return this.submit(source, opts?.petRef !== void 0 ? { petRef: opts.petRef } : void 0);
1085
- }
1086
- /** Capture -> presigned PUT -> POST /video/analyze -> poll. Returns the JobId or a domain error. */
1087
- async submit(source, opts) {
1088
- const op = this.beginOperation(opts?.signal);
1089
- const petRef = opts?.petRef ?? this.defaultPetRef;
1090
- const idempotencyKey = this.makeIdempotencyKey();
1091
- const startedAt = this.timers.now();
1092
- this.patch({ status: "uploading", progress: 0, error: null, result: null, job: null });
1093
- try {
1094
- const initiated = await initiateUpload(this.funnel, {
1095
- purpose: "gait_video",
1096
- contentType: source.mimeType,
1097
- byteSize: mediaByteSize(source),
1098
- signal: op.signal
1099
- });
1100
- if (op.signal.aborted || !this.alive(op.epoch)) return abortedResult();
1101
- if (!initiated.ok) return this.fail(op, initiated.error);
1102
- const staged = await stageUpload(this.upload, source, initiated.value, idempotencyKey, op.signal);
1103
- if (op.signal.aborted || !this.alive(op.epoch)) return abortedResult();
1104
- if (!staged.ok) {
1105
- this.telemetry.track("analysis.upload", {
1106
- capability: "video",
1107
- purpose: "gait_video",
1108
- outcome: "failed"
1109
- });
1110
- return this.fail(op, staged.error);
1111
- }
1112
- this.patch({ progress: 1 });
1113
- this.telemetry.track("analysis.upload", {
1114
- capability: "video",
1115
- purpose: "gait_video",
1116
- byteSize: mediaByteSize(source) ?? 0,
1117
- durationMs: mediaDurationMs(source) ?? 0,
1118
- outcome: "ok"
1119
- });
1120
- this.patch({ status: "analyzing" });
1121
- const outcome = await submitAnalyze(
1122
- this.funnel,
1123
- ENDPOINTS.videoAnalyze,
1124
- analyzeBody(initiated.value.s3Key, { petRef }),
1125
- detectNoGate,
1126
- op.signal
1127
- );
1128
- if (op.signal.aborted || !this.alive(op.epoch)) return abortedResult();
1129
- if (outcome.kind === "error") return this.fail(op, outcome.error);
1130
- if (outcome.kind === "gate") return this.fail(op, makeEverfurError("unknown", { retryable: false }));
1131
- if (outcome.kind === "sync") {
1132
- const jobId = brandJobId(resultIdOf(outcome.body) ?? "");
1133
- return this.complete(op, outcome.body, jobId, startedAt);
1134
- }
1135
- const job = outcome.job;
1136
- this.patch({ job: { id: job.jobId, state: "processing" } });
1137
- const poll = await pollJob(this.funnel, job.jobId, {
1138
- timers: this.timers,
1139
- signal: op.signal,
1140
- alive: () => this.alive(op.epoch),
1141
- estimatedSeconds: job.estimatedSeconds
1142
- });
1143
- if (op.signal.aborted || !this.alive(op.epoch)) return abortedResult();
1144
- switch (poll.kind) {
1145
- case "aborted":
1146
- return abortedResult();
1147
- case "complete":
1148
- return this.complete(op, poll.body, job.jobId, startedAt);
1149
- case "failed":
1150
- return this.fail(op, poll.error);
1151
- case "timeout":
1152
- return this.fail(op, makeEverfurError("streamTimeout", { retryable: true }));
1153
- case "notFound":
1154
- this.patch({ job: null });
1155
- return this.fail(op, makeEverfurError("conversationNotFound", { retryable: false, httpStatus: 404 }));
1156
- default:
1157
- return this.fail(op, makeEverfurError("internalError", { retryable: false }));
1158
- }
1159
- } finally {
1160
- op.settle();
1161
- }
1162
- }
1163
- /** Abort upload/poll; status returns to idle (aborted !== error). */
1164
- cancel() {
1165
- this.inFlight?.abort();
1166
- this.inFlight = null;
1167
- if (this.snapshot.status === "uploading" || this.snapshot.status === "analyzing") {
1168
- this.patch({ status: "idle", progress: 0 });
1169
- }
1170
- }
1171
- // ---- terminal transitions ---------------------------------------------
1172
- complete(op, body, jobId, startedAt) {
1173
- if (!this.alive(op.epoch)) return abortedResult();
1174
- const mapped = mapVideoResult(body, jobId);
1175
- if (mapped.droppedOperatorKeys > 0) {
1176
- this.telemetry.track("analysis.metadata_leak_guard", { droppedKeyCount: mapped.droppedOperatorKeys });
1177
- }
1178
- this.telemetry.track("analysis.quality_gate", {
1179
- usable: mapped.result.quality.usable,
1180
- reasons: mapped.result.quality.reasons
1181
- });
1182
- this.patch({
1183
- status: "complete",
1184
- progress: 1,
1185
- result: mapped.result,
1186
- error: null,
1187
- job: { id: jobId, state: "complete" }
1188
- });
1189
- this.telemetry.track("analysis.result", {
1190
- capability: "video",
1191
- modelVersion: mapped.result.modelVersion ?? "",
1192
- timeToResultMs: this.timers.now() - startedAt
1193
- });
1194
- return ok(jobId);
1195
- }
1196
- fail(op, error) {
1197
- if (!this.alive(op.epoch)) return abortedResult();
1198
- this.patch({ status: "error", error, progress: 0 });
1199
- this.telemetry.trackExpectedFailure("analysis.error", {
1200
- code: error.code,
1201
- ...error.httpStatus !== void 0 ? { httpStatus: error.httpStatus } : {}
1202
- });
1203
- return err(error);
1204
- }
1205
- };
1206
- function mapVideoResult(body, jobId) {
1207
- const projection = projectDetectionMetadata(locateDetectionMetadata(body));
1208
- const interpretation = parseInterpretation(locateInterpretation(body));
1209
- const modelVersion = asString(projection.metadata["model_version"]) ?? (isRecord2(body) ? asString(body["model_version"]) : null);
1210
- const result = Object.freeze({
1211
- jobId,
1212
- analysisType: "gait",
1213
- detections: parseVideoDetections(body),
1214
- interpretation,
1215
- modelVersion,
1216
- quality: parseQuality(body)
1217
- });
1218
- return { result, droppedOperatorKeys: projection.droppedOperatorKeys };
1219
- }
1220
- function parseVideoDetections(body) {
1221
- const raw = isRecord2(body) ? body["detections"] : null;
1222
- if (!Array.isArray(raw)) return Object.freeze([]);
1223
- const out = [];
1224
- for (const entry of raw) {
1225
- if (!isRecord2(entry)) continue;
1226
- const label = asString(entry["label"]) ?? asString(entry["name"]);
1227
- if (label === null) continue;
1228
- out.push(
1229
- Object.freeze({
1230
- label,
1231
- confidence: asUnitNumber(entry["confidence"]) ?? 0,
1232
- timestampMs: asFiniteNumber(entry["timestamp_ms"] ?? entry["timestamp"]),
1233
- durationMs: asFiniteNumber(entry["duration_ms"] ?? entry["duration"])
1234
- })
1235
- );
1236
- }
1237
- return Object.freeze(out);
1238
- }
1239
- function parseQuality(body) {
1240
- const raw = isRecord2(body) ? body["quality"] : null;
1241
- if (!isRecord2(raw)) return Object.freeze({ usable: true, reasons: Object.freeze([]) });
1242
- const usable = raw["usable"];
1243
- return Object.freeze({
1244
- usable: typeof usable === "boolean" ? usable : true,
1245
- reasons: Object.freeze(asStringList(raw["reasons"]))
1246
- });
1247
- }
1248
- function resultIdOf(body) {
1249
- if (!isRecord2(body)) return null;
1250
- return asString(body["result_id"]) ?? asString(body["request_id"]) ?? asString(body["job_id"]);
1251
- }
1252
- function abortedResult() {
1253
- return err(makeEverfurError("unknown", { retryable: false }));
1254
- }
1255
- function defaultIdempotencyFactory() {
1256
- let counter = 0;
1257
- return () => `video-analyze-${Date.now().toString(36)}-${(counter++).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
1258
- }
1259
-
1260
- // src/client/entitlements/resolve.ts
1261
- var REQUIRES = Object.freeze({
1262
- chat: Object.freeze(["chat.message.create"]),
1263
- records: Object.freeze(["records.upload.create", "records.record.read"]),
1264
- video: Object.freeze(["video.gait.execute"]),
1265
- photo: Object.freeze(["photo.checkup.execute"]),
1266
- animations: Object.freeze(["animations.state.read"])
1267
- });
1268
- function requires(name) {
1269
- return REQUIRES[name];
1270
- }
1271
- function decideCapability(name, decisions, opts) {
1272
- if (opts?.isPending === true) {
1273
- return freezeDecision({ name, enabled: false, isPending: true, limits: {} });
1274
- }
1275
- const keys = requires(name);
1276
- const byResource = /* @__PURE__ */ new Map();
1277
- for (const decision of decisions) byResource.set(decision.resource, decision);
1278
- const limits = mergeLimits(keys, byResource);
1279
- const missing = keys.filter((key) => byResource.get(key)?.granted !== true);
1280
- if (missing.length === 0) {
1281
- return freezeDecision({ name, enabled: true, isPending: false, limits });
1282
- }
1283
- return freezeDecision({
1284
- name,
1285
- enabled: false,
1286
- isPending: false,
1287
- limits,
1288
- disabled: disabledFor(keys, byResource)
1289
- });
1290
- }
1291
- function mergeLimits(keys, byResource) {
1292
- const out = {};
1293
- for (const key of keys) {
1294
- const decisionLimits = byResource.get(key)?.limits;
1295
- if (!decisionLimits) continue;
1296
- for (const [limitKey, limitValue] of Object.entries(decisionLimits)) {
1297
- const existing = out[limitKey];
1298
- out[limitKey] = existing === void 0 ? limitValue : Math.min(existing, limitValue);
1299
- }
1300
- }
1301
- return out;
1302
- }
1303
- function disabledFor(keys, byResource) {
1304
- for (const key of keys) {
1305
- const explicit = byResource.get(key)?.disabled;
1306
- if (explicit) {
1307
- return Object.freeze({
1308
- reason: explicit.reason,
1309
- level: explicit.level,
1310
- recoverable: explicit.recoverable
1311
- });
1312
- }
1313
- }
1314
- return Object.freeze({
1315
- reason: userFacingError("accessDenied"),
1316
- level: "user_entitlement",
1317
- recoverable: false
1318
- });
1319
- }
1320
- function freezeDecision(d) {
1321
- const base = {
1322
- name: d.name,
1323
- enabled: d.enabled,
1324
- isPending: d.isPending,
1325
- limits: Object.freeze({ ...d.limits })
1326
- };
1327
- if (d.disabled !== void 0) base.disabled = d.disabled;
1328
- return Object.freeze(base);
1329
- }
1330
-
1331
- // src/client/theme/tokens.ts
1332
- function deepFreeze(value) {
1333
- if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
1334
- Object.freeze(value);
1335
- for (const key of Object.keys(value)) {
1336
- deepFreeze(value[key]);
1337
- }
1338
- }
1339
- return value;
1340
- }
1341
- var SPACE = { xs: 4, sm: 8, md: 12, lg: 16, xl: 24, xxl: 32 };
1342
- var RADIUS = { tile: 5, xs: 6, sm: 10, md: 14, lg: 20, xl: 28, pill: 500 };
1343
- var TYPE_SIZE = {
1344
- nano: 8,
1345
- small: 9,
1346
- micro: 10,
1347
- tiny: 11,
1348
- caption: 12,
1349
- bodyTight: 13,
1350
- body: 14,
1351
- subtitle: 16,
1352
- heading: 18,
1353
- verdict: 24
1354
- };
1355
- var LINE_HEIGHT = { tight: 1.15, snug: 1.3, normal: 1.4, relaxed: 1.55 };
1356
- var WEIGHT = { regular: "400", medium: "500", bold: "700" };
1357
- var LETTER_SPACING = {
1358
- eyebrow: 1,
1359
- monoWide: 0.9,
1360
- headerBrand: -0.42,
1361
- tight: -0.35
1362
- };
1363
- function baseType() {
1364
- return {
1365
- family: "System",
1366
- mono: "monospace",
1367
- fontUrl: null,
1368
- size: TYPE_SIZE,
1369
- lineHeight: LINE_HEIGHT,
1370
- weight: WEIGHT,
1371
- letterSpacing: LETTER_SPACING
1372
- };
1373
- }
1374
- var MOTION = {
1375
- duration: { instant: 80, fast: 140, normal: 220, slow: 320 },
1376
- spring: { gentle: { damping: 18, stiffness: 200 } },
1377
- easing: { standard: "cubic-bezier(0.4, 0, 0.2, 1)" },
1378
- reducedMotion: false
1379
- };
1380
- var LIGHT_COLOR = {
1381
- brand: "#10462E",
1382
- brandSoft: "rgba(16,70,46,0.10)",
1383
- brandSofter: "rgba(16,70,46,0.15)",
1384
- brandBorder: "rgba(16,70,46,0.18)",
1385
- borderHairlineGreen: "rgba(16,70,46,0.25)",
1386
- surface: "#FFFFFF",
1387
- surfaceCream: "#FDFBF8",
1388
- surfaceSubtle: "#EFE9E3",
1389
- background: "#FFFFFF",
1390
- borderRingCream: "#D8D2C8",
1391
- text: {
1392
- primary: "#111111",
1393
- secondary: "#0E462E",
1394
- muted: "#6E6660",
1395
- placeholder: "rgba(0,0,0,0.30)",
1396
- onBrand: "#FFFFFF",
1397
- metaCool: "#3D3D3D"
1398
- },
1399
- primary: "#10462E",
1400
- onPrimary: "#FFFFFF",
1401
- accent: "#10462E",
1402
- onAccent: "#FFFFFF",
1403
- accentEdge: "#07C168",
1404
- success: "#07C168",
1405
- danger: "#B91C1C",
1406
- errorRed: "#FF1B00",
1407
- warningStrong: "#A45610",
1408
- cautionAccent: "#D9821D",
1409
- riskConcern: "#EFA700",
1410
- badgeBgPositive: "rgba(7,193,104,0.12)",
1411
- badgeBgCaution: "rgba(217,130,29,0.14)",
1412
- badgeBgCritical: "rgba(185,28,28,0.12)",
1413
- badgeBgNeutral: "rgba(110,102,96,0.12)",
1414
- bubbleUser: "#10462E",
1415
- bubbleAssistant: "#FFFFFF",
1416
- skeleton: "#EFE9E3",
1417
- scanButtonPink: "#FFC3BC",
1418
- checkupCardLowRiskBg: "rgba(7,193,104,0.10)",
1419
- checkupCardConcernBg: "rgba(239,167,0,0.10)"
1420
- };
1421
- var EVERFUR_LIGHT = deepFreeze({
1422
- brightness: "light",
1423
- meta: { displayName: "Everfur", logoUrl: null, placeholder: null, hidePoweredBy: false },
1424
- color: LIGHT_COLOR,
1425
- space: SPACE,
1426
- radius: RADIUS,
1427
- type: baseType(),
1428
- motion: MOTION
1429
- });
1430
- var DARK_COLOR = {
1431
- brand: "#6BD39A",
1432
- brandSoft: "rgba(107,211,154,0.12)",
1433
- brandSofter: "rgba(107,211,154,0.18)",
1434
- brandBorder: "rgba(107,211,154,0.22)",
1435
- borderHairlineGreen: "rgba(107,211,154,0.28)",
1436
- surface: "#1C1F23",
1437
- surfaceCream: "#23272C",
1438
- surfaceSubtle: "#23272C",
1439
- background: "#15171A",
1440
- borderRingCream: "#3A3F45",
1441
- text: {
1442
- primary: "#F5F3F0",
1443
- secondary: "#A9E4C4",
1444
- muted: "#9A938C",
1445
- placeholder: "rgba(255,255,255,0.35)",
1446
- onBrand: "#0E1F17",
1447
- metaCool: "#C9C9C9"
1448
- },
1449
- primary: "#6BD39A",
1450
- onPrimary: "#0E1F17",
1451
- accent: "#6BD39A",
1452
- onAccent: "#0E1F17",
1453
- accentEdge: "#34D888",
1454
- success: "#07C168",
1455
- danger: "#B91C1C",
1456
- errorRed: "#FF1B00",
1457
- warningStrong: "#A45610",
1458
- cautionAccent: "#D9821D",
1459
- riskConcern: "#EFA700",
1460
- badgeBgPositive: "rgba(7,193,104,0.18)",
1461
- badgeBgCaution: "rgba(217,130,29,0.20)",
1462
- badgeBgCritical: "rgba(185,28,28,0.22)",
1463
- badgeBgNeutral: "rgba(255,255,255,0.10)",
1464
- bubbleUser: "#6BD39A",
1465
- bubbleAssistant: "#1C1F23",
1466
- skeleton: "#23272C",
1467
- scanButtonPink: "#FFC3BC",
1468
- checkupCardLowRiskBg: "rgba(7,193,104,0.16)",
1469
- checkupCardConcernBg: "rgba(239,167,0,0.16)"
1470
- };
1471
- deepFreeze({
1472
- brightness: "dark",
1473
- meta: { displayName: "Everfur", logoUrl: null, placeholder: null, hidePoweredBy: false },
1474
- color: DARK_COLOR,
1475
- space: SPACE,
1476
- radius: RADIUS,
1477
- type: baseType(),
1478
- motion: MOTION
1479
- });
1480
- var EVERFUR_THEME_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("@everfur/sdk#EverfurThemeContext@v1");
1481
- var globalThemeHost = globalThis;
1482
- var EverfurThemeContext = globalThemeHost[EVERFUR_THEME_CONTEXT_SYMBOL] ?? (globalThemeHost[EVERFUR_THEME_CONTEXT_SYMBOL] = react.createContext(EVERFUR_LIGHT));
1483
- EverfurThemeContext.displayName ??= "EverfurThemeContext";
1484
- function useEverfurTheme() {
1485
- return react.useContext(EverfurThemeContext);
1486
- }
1487
-
1488
- // src/rn/facade.ts
1489
- var EVERFUR_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("@everfur/sdk#EverfurContext@v1");
1490
- var globalContextHost = globalThis;
1491
- var EverfurContext = globalContextHost[EVERFUR_CONTEXT_SYMBOL] ?? (globalContextHost[EVERFUR_CONTEXT_SYMBOL] = react.createContext(null));
1492
- EverfurContext.displayName ??= "EverfurContext";
1493
- function useEverfurContext() {
1494
- const ctx = react.useContext(EverfurContext);
1495
- if (ctx === null) {
1496
- throw new EverfurConfigError(
1497
- "not-initialized",
1498
- "Everfur hooks must be called inside <EverfurProvider>. Wrap your tree once, high."
1499
- );
1500
- }
1501
- return ctx;
1502
- }
1503
-
1504
- // src/rn/video/useVideo.ts
1505
- var BOOTSTRAP_SNAPSHOT = Object.freeze({
1506
- status: "idle",
1507
- job: null,
1508
- result: null,
1509
- error: null,
1510
- isCaptureAvailable: false,
1511
- progress: 0
1512
- });
1513
- var NOOP_UNSUBSCRIBE = () => {
1514
- };
1515
- function useEverfurVideo(opts) {
1516
- const { runtime, userRef, activePet, scopeEpoch } = useEverfurContext();
1517
- const petRef = opts?.petRef ?? activePet ?? null;
1518
- const [controller, setController] = react.useState(null);
1519
- react.useEffect(() => {
1520
- const { services } = runtime.registry;
1521
- const acquired = runtime.registry.acquire(
1522
- "video",
1523
- `${userRef ?? ""}|${petRef ?? ""}`,
1524
- () => createVideoController({
1525
- ...services.errorPolicy ? { errorPolicy: services.errorPolicy } : {},
1526
- auth: services.authFor(userRef),
1527
- camera: services.camera,
1528
- upload: services.upload,
1529
- petRef,
1530
- telemetry: services.telemetry
1531
- }),
1532
- (controller2) => controller2.dispose()
1533
- );
1534
- setController(acquired);
1535
- return () => {
1536
- setController((current) => current === acquired ? null : current);
1537
- runtime.registry.release(acquired);
1538
- };
1539
- }, [runtime, userRef, petRef, scopeEpoch]);
1540
- const subscribe = react.useCallback(
1541
- (onChange) => controller ? controller.subscribe(onChange) : NOOP_UNSUBSCRIBE,
1542
- [controller]
1543
- );
1544
- const getSnapshot = react.useCallback(
1545
- () => controller ? controller.getSnapshot() : BOOTSTRAP_SNAPSHOT,
1546
- [controller]
1547
- );
1548
- const snapshot = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
1549
- const submit = react.useCallback(
1550
- (source) => controller ? controller.submit(source) : Promise.resolve(notReadyError()),
1551
- [controller]
1552
- );
1553
- const capture = react.useCallback(
1554
- () => controller ? controller.capture() : Promise.resolve(notReadyError()),
1555
- [controller]
1556
- );
1557
- const cancel = react.useCallback(() => {
1558
- controller?.cancel();
1559
- }, [controller]);
1560
- return react.useMemo(
1561
- () => ({
1562
- status: snapshot.status,
1563
- job: snapshot.job,
1564
- result: snapshot.result,
1565
- error: snapshot.error,
1566
- isCaptureAvailable: snapshot.isCaptureAvailable,
1567
- progress: snapshot.progress,
1568
- isBootstrapping: controller === null,
1569
- submit,
1570
- capture,
1571
- cancel
1572
- }),
1573
- [snapshot, controller, submit, capture, cancel]
1574
- );
1575
- }
1576
- function notReadyError() {
1577
- return err(
1578
- makeEverfurError("internalError", { retryable: true })
1579
- );
1580
- }
1581
- function useCapability(name) {
1582
- const { runtime } = useEverfurContext();
1583
- const store = runtime.entitlements;
1584
- const snapshot = react.useSyncExternalStore(
1585
- store.subscribe,
1586
- store.getSnapshot,
1587
- store.getSnapshot
1588
- );
1589
- return react.useMemo(
1590
- () => decideCapability(name, snapshot.decisions, { isPending: snapshot.isPending }),
1591
- [name, snapshot]
1592
- );
1593
- }
1594
- var MIN_OFFSTATE_HEIGHT = 48;
1595
- function selectView(decision) {
1596
- if (decision.isPending) return "pending";
1597
- return decision.enabled ? "enabled" : "disabled";
1598
- }
1599
- function CapabilityGate(props) {
1600
- const { capability, children, renderDisabled, renderPending, style } = props;
1601
- const decision = useCapability(capability);
1602
- const view = react.useMemo(() => selectView(decision), [decision]);
1603
- switch (view) {
1604
- case "enabled":
1605
- return children;
1606
- case "pending":
1607
- return renderPending ? renderPending() : /* @__PURE__ */ jsxRuntime.jsx(PendingSkeleton, { capability, style });
1608
- case "disabled":
1609
- return renderDisabled ? renderDisabled(decision) : /* @__PURE__ */ jsxRuntime.jsx(OffState, { decision, style });
1610
- default:
1611
- return assertNever(view);
1612
- }
1613
- }
1614
- function PendingSkeleton({
1615
- capability,
1616
- style
1617
- }) {
1618
- const theme = useEverfurTheme();
1619
- const styles = react.useMemo(() => makeStyles(theme), [theme]);
1620
- return /* @__PURE__ */ jsxRuntime.jsx(
1621
- reactNative.View,
1622
- {
1623
- style: [styles.offstate, style],
1624
- accessible: true,
1625
- accessibilityState: { busy: true },
1626
- accessibilityLabel: `Loading ${capability}`,
1627
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.skeletonBar })
1628
- }
1629
- );
1630
- }
1631
- function OffState({
1632
- decision,
1633
- style
1634
- }) {
1635
- const theme = useEverfurTheme();
1636
- const styles = react.useMemo(() => makeStyles(theme), [theme]);
1637
- const message = decision.disabled?.reason ?? "";
1638
- return /* @__PURE__ */ jsxRuntime.jsx(
1639
- reactNative.View,
1640
- {
1641
- style: [styles.offstate, style],
1642
- accessible: true,
1643
- accessibilityRole: "text",
1644
- accessibilityLabel: message,
1645
- children: message.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.offstateText, children: message }) : null
1646
- }
1647
- );
1648
- }
1649
- function makeStyles(theme) {
1650
- const { color, space, radius, type } = theme;
1651
- return reactNative.StyleSheet.create({
1652
- // A deliberate cream card with a soft brand hairline, never a bare grey slab.
1653
- offstate: {
1654
- minHeight: MIN_OFFSTATE_HEIGHT,
1655
- justifyContent: "center",
1656
- paddingHorizontal: space.lg,
1657
- paddingVertical: space.md,
1658
- backgroundColor: color.surfaceCream,
1659
- borderRadius: radius.md,
1660
- borderWidth: reactNative.StyleSheet.hairlineWidth,
1661
- borderColor: color.brandBorder
1662
- },
1663
- offstateText: {
1664
- fontFamily: type.family,
1665
- fontSize: type.size.body,
1666
- lineHeight: Math.round(type.size.body * type.lineHeight.normal),
1667
- color: color.text.muted
1668
- },
1669
- // A soft green shimmer bar (brand wash) that stays legible on the cream card in both light and dark,
1670
- // where the neutral `skeleton` token would collide with the dark cream surface.
1671
- skeletonBar: {
1672
- height: space.md,
1673
- borderRadius: radius.xs,
1674
- backgroundColor: color.brandSofter
1675
- }
1676
- });
1677
- }
1678
- var CLINICAL_DISCLAIMER = Object.freeze({
1679
- title: "For educational use only",
1680
- body: "Everfur is for educational purposes only. It helps you track visible changes through AI checkups. It does not diagnose, treat, prescribe, or replace veterinary care. For urgent symptoms or emergencies, contact a veterinarian immediately."
1681
- });
1682
- function ClinicalDisclaimer(props) {
1683
- const theme = useEverfurTheme();
1684
- const styles = react.useMemo(() => makeStyles2(theme), [theme]);
1685
- return /* @__PURE__ */ jsxRuntime.jsxs(
1686
- reactNative.View,
1687
- {
1688
- style: [styles.container, props.style],
1689
- accessible: true,
1690
- accessibilityRole: "text",
1691
- accessibilityLabel: `${CLINICAL_DISCLAIMER.title}. ${CLINICAL_DISCLAIMER.body}`,
1692
- children: [
1693
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.title, children: CLINICAL_DISCLAIMER.title }),
1694
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.body, children: CLINICAL_DISCLAIMER.body })
1695
- ]
1696
- }
1697
- );
1698
- }
1699
- function makeStyles2(theme) {
1700
- const { color, space, type } = theme;
1701
- return reactNative.StyleSheet.create({
1702
- container: {
1703
- paddingHorizontal: space.lg,
1704
- paddingVertical: space.md,
1705
- borderTopWidth: reactNative.StyleSheet.hairlineWidth,
1706
- borderTopColor: color.borderHairlineGreen,
1707
- backgroundColor: color.surfaceCream,
1708
- gap: space.xs
1709
- },
1710
- title: {
1711
- fontFamily: type.family,
1712
- fontSize: type.size.caption,
1713
- lineHeight: Math.round(type.size.caption * type.lineHeight.snug),
1714
- color: color.text.secondary,
1715
- fontWeight: type.weight.bold,
1716
- letterSpacing: type.letterSpacing.eyebrow
1717
- },
1718
- body: {
1719
- fontFamily: type.family,
1720
- fontSize: type.size.caption,
1721
- lineHeight: Math.round(type.size.caption * type.lineHeight.snug),
1722
- color: color.text.muted,
1723
- fontWeight: type.weight.regular
1724
- }
1725
- });
1726
- }
1727
- var SPINNER_DELAY_MS = 300;
1728
- var MIN_SURFACE_HEIGHT = 120;
1729
- var TOUCH_TARGET = 44;
1730
- var GLYPH_SIZE = 40;
1731
- var PROGRESS_TRACK_WIDTH = 160;
1732
- function EverfurVideo(props) {
1733
- const { petRef, style } = props;
1734
- return /* @__PURE__ */ jsxRuntime.jsx(CapabilityGate, { capability: "video", style, children: /* @__PURE__ */ jsxRuntime.jsx(VideoBody, { petRef, style }) });
1735
- }
1736
- function selectView2(state) {
1737
- if (state.isBootstrapping) return "loading";
1738
- if (state.status === "uploading" || state.status === "analyzing") return "loading";
1739
- if (state.status === "error" && state.error) return "error";
1740
- if (state.status === "complete" && state.result) return "populated";
1741
- if (!state.isCaptureAvailable) return "native";
1742
- return "empty";
1743
- }
1744
- function VideoBody({
1745
- petRef,
1746
- style
1747
- }) {
1748
- const videoOpts = react.useMemo(() => petRef !== void 0 ? { petRef } : void 0, [petRef]);
1749
- const video = useEverfurVideo(videoOpts);
1750
- const view = selectView2(video);
1751
- const { styles } = useVideoStyles();
1752
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.container, style], children: [
1753
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.body, children: renderView(view, video) }),
1754
- /* @__PURE__ */ jsxRuntime.jsx(ClinicalDisclaimer, {})
1755
- ] });
1756
- }
1757
- function renderView(view, video) {
1758
- switch (view) {
1759
- case "loading":
1760
- return /* @__PURE__ */ jsxRuntime.jsx(LoadingState, { progress: video.progress });
1761
- case "native":
1762
- return /* @__PURE__ */ jsxRuntime.jsx(NativeUnavailableState, {});
1763
- case "empty":
1764
- return /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { onCapture: video.capture });
1765
- case "error":
1766
- return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { error: video.error, onRetry: video.capture });
1767
- case "populated":
1768
- return /* @__PURE__ */ jsxRuntime.jsx(ResultState, { result: video.result });
1769
- default:
1770
- return assertNever(view);
1771
- }
1772
- }
1773
- function LoadingState({ progress }) {
1774
- const { theme, styles } = useVideoStyles();
1775
- const [visible, setVisible] = react.useState(false);
1776
- react.useEffect(() => {
1777
- const id = setTimeout(() => setVisible(true), SPINNER_DELAY_MS);
1778
- return () => clearTimeout(id);
1779
- }, []);
1780
- const pct = Math.max(0, Math.min(1, progress));
1781
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.centered, accessible: true, accessibilityState: { busy: true }, accessibilityLabel: "Analyzing video", children: [
1782
- visible ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: theme.color.brand }) : null,
1783
- visible && pct > 0 && pct < 1 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.progressTrack, accessibilityRole: "progressbar", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.progressFill, { width: `${Math.round(pct * 100)}%` }] }) }) : null
1784
- ] });
1785
- }
1786
- function NativeUnavailableState() {
1787
- const { styles } = useVideoStyles();
1788
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.offstate, accessible: true, accessibilityRole: "text", accessibilityLabel: "Camera is unavailable", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.emptyGlyph }) });
1789
- }
1790
- function EmptyState({ onCapture }) {
1791
- const { styles } = useVideoStyles();
1792
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.centered, accessible: true, accessibilityRole: "summary", accessibilityLabel: "Record a short clip to check gait", children: /* @__PURE__ */ jsxRuntime.jsx(
1793
- reactNative.Pressable,
1794
- {
1795
- onPress: onCapture,
1796
- accessibilityRole: "button",
1797
- accessibilityLabel: "Record video",
1798
- style: styles.primaryButton,
1799
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.primaryLabel, children: "Record video" })
1800
- }
1801
- ) });
1802
- }
1803
- function ErrorState({ error, onRetry }) {
1804
- const { styles } = useVideoStyles();
1805
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.centered, accessible: true, accessibilityRole: "alert", children: [
1806
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.errorText, children: error.displayMessage }),
1807
- error.retryable ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onRetry, accessibilityRole: "button", accessibilityLabel: "Try again", style: styles.primaryButton, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.primaryLabel, children: "Try again" }) }) : null
1808
- ] });
1809
- }
1810
- function ResultState({ result }) {
1811
- const { styles } = useVideoStyles();
1812
- if (!result.quality.usable) {
1813
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.centered, accessible: true, accessibilityRole: "summary", accessibilityLabel: "Result uncertain", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.uncertainText, children: "We could not get a clear read from this clip. Try recording again." }) });
1814
- }
1815
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.ScrollView, { style: styles.list, contentContainerStyle: styles.listContent, accessibilityLabel: "Gait analysis result", children: [
1816
- result.detections.map((detection, index) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.resultRow, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.resultLabel, children: detection.label }) }, `${detection.label}-${index}`)),
1817
- result.interpretation ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.interpretationBlock, children: [
1818
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.summaryText, children: result.interpretation.summary }),
1819
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.disclaimerText, children: result.interpretation.disclaimer })
1820
- ] }) : null
1821
- ] });
1822
- }
1823
- function useVideoStyles() {
1824
- const theme = useEverfurTheme();
1825
- return react.useMemo(() => ({ theme, styles: makeStyles3(theme) }), [theme]);
1826
- }
1827
- function makeStyles3(theme) {
1828
- const { color, space, radius, type } = theme;
1829
- const boldWeight = type.weight.bold;
1830
- const mediumWeight = type.weight.medium;
1831
- return reactNative.StyleSheet.create({
1832
- // Themed ground so the surface is never a bare/transparent region and holds contrast in both schemes.
1833
- container: { flex: 1, minHeight: MIN_SURFACE_HEIGHT, backgroundColor: color.background },
1834
- body: { flex: 1 },
1835
- centered: {
1836
- minHeight: MIN_SURFACE_HEIGHT,
1837
- flexGrow: 1,
1838
- alignItems: "center",
1839
- justifyContent: "center",
1840
- padding: space.xl
1841
- },
1842
- offstate: {
1843
- minHeight: MIN_SURFACE_HEIGHT,
1844
- alignItems: "center",
1845
- justifyContent: "center",
1846
- padding: space.xl
1847
- },
1848
- // Camera-unavailable placeholder: a cream disc ringed with the brand hairline (intentional, not a grey dot).
1849
- emptyGlyph: {
1850
- width: GLYPH_SIZE,
1851
- height: GLYPH_SIZE,
1852
- borderRadius: radius.pill,
1853
- backgroundColor: color.surfaceCream,
1854
- borderWidth: reactNative.StyleSheet.hairlineWidth,
1855
- borderColor: color.borderHairlineGreen
1856
- },
1857
- // Capture / retry CTA: brand-green pill on a soft brand shadow (alpha baked into the token color).
1858
- primaryButton: {
1859
- minHeight: TOUCH_TARGET,
1860
- minWidth: TOUCH_TARGET,
1861
- alignItems: "center",
1862
- justifyContent: "center",
1863
- paddingHorizontal: space.xl,
1864
- paddingVertical: space.md,
1865
- borderRadius: radius.pill,
1866
- backgroundColor: color.primary,
1867
- shadowColor: color.brandSofter,
1868
- shadowOffset: { width: 0, height: space.xs },
1869
- shadowOpacity: 1,
1870
- shadowRadius: space.sm,
1871
- elevation: space.xs
1872
- },
1873
- primaryLabel: {
1874
- color: color.onPrimary,
1875
- fontFamily: type.family,
1876
- fontSize: type.size.body,
1877
- fontWeight: boldWeight,
1878
- letterSpacing: type.letterSpacing.tight
1879
- },
1880
- errorText: {
1881
- color: color.danger,
1882
- fontFamily: type.family,
1883
- fontSize: type.size.subtitle,
1884
- lineHeight: type.size.subtitle * type.lineHeight.normal,
1885
- fontWeight: mediumWeight,
1886
- textAlign: "center",
1887
- marginBottom: space.md
1888
- },
1889
- uncertainText: {
1890
- color: color.text.muted,
1891
- fontFamily: type.family,
1892
- fontSize: type.size.subtitle,
1893
- lineHeight: type.size.subtitle * type.lineHeight.normal,
1894
- textAlign: "center"
1895
- },
1896
- // Progress groove (skeleton wash) with a brand-green fill -- replaces the grey-track / blue-fill default.
1897
- progressTrack: {
1898
- marginTop: space.lg,
1899
- height: space.sm,
1900
- width: PROGRESS_TRACK_WIDTH,
1901
- borderRadius: radius.pill,
1902
- backgroundColor: color.skeleton,
1903
- overflow: "hidden"
1904
- },
1905
- progressFill: { height: space.sm, borderRadius: radius.pill, backgroundColor: color.brand },
1906
- list: { flex: 1 },
1907
- listContent: { padding: space.lg, gap: space.md },
1908
- // Detection rows: cream cards lifted off the ground by a hairline brand border (depth via layering).
1909
- resultRow: {
1910
- paddingVertical: space.sm,
1911
- paddingHorizontal: space.md,
1912
- borderRadius: radius.sm,
1913
- backgroundColor: color.surfaceCream,
1914
- borderWidth: reactNative.StyleSheet.hairlineWidth,
1915
- borderColor: color.brandBorder
1916
- },
1917
- resultLabel: {
1918
- color: color.text.primary,
1919
- fontFamily: type.family,
1920
- fontSize: type.size.body,
1921
- lineHeight: type.size.body * type.lineHeight.normal,
1922
- fontWeight: mediumWeight
1923
- },
1924
- interpretationBlock: { marginTop: space.sm, gap: space.sm },
1925
- summaryText: {
1926
- color: color.text.primary,
1927
- fontFamily: type.family,
1928
- fontSize: type.size.subtitle,
1929
- lineHeight: type.size.subtitle * type.lineHeight.normal
1930
- },
1931
- disclaimerText: {
1932
- color: color.text.muted,
1933
- fontFamily: type.family,
1934
- fontSize: type.size.caption,
1935
- lineHeight: type.size.caption * type.lineHeight.relaxed
1936
- }
1937
- });
1938
- }
1939
-
1940
- exports.EverfurVideo = EverfurVideo;
1941
- exports.useEverfurVideo = useEverfurVideo;
1
+ 'use strict';var react=require('react'),reactNative=require('react-native'),jsxRuntime=require('react/jsx-runtime');var Ur=Object.defineProperty;var o=(e,r)=>Ur(e,"name",{value:r,configurable:true});var re=Object.freeze([Object.freeze({wireCode:"AccessDeniedException",envelope:"http_envelope",httpStatus:403,retryable:false}),Object.freeze({wireCode:"AuthorizationException",envelope:"http_envelope",httpStatus:401,retryable:false}),Object.freeze({wireCode:"ConflictException",envelope:"http_envelope",httpStatus:409,retryable:true}),Object.freeze({wireCode:"DAILY_QUOTA_EXCEEDED",envelope:"http_envelope",httpStatus:429,retryable:true}),Object.freeze({wireCode:"FILE_TOO_LARGE",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"IDEMPOTENCY_KEY_IN_FLIGHT",envelope:"http_envelope",httpStatus:409,retryable:true}),Object.freeze({wireCode:"IDEMPOTENCY_KEY_REUSED_DIFFERENT_REQUEST",envelope:"http_envelope",httpStatus:409,retryable:false}),Object.freeze({wireCode:"INVALID_CONTENT_TYPE",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"InternalFailureException",envelope:"http_envelope",httpStatus:500,retryable:true}),Object.freeze({wireCode:"MethodNotAllowedException",envelope:"http_envelope",httpStatus:405,retryable:false}),Object.freeze({wireCode:"NotImplementedException",envelope:"http_envelope",httpStatus:501,retryable:false}),Object.freeze({wireCode:"PaymentRequiredException",envelope:"http_envelope",httpStatus:402,retryable:false}),Object.freeze({wireCode:"ResourceNotFoundException",envelope:"http_envelope",httpStatus:404,retryable:false}),Object.freeze({wireCode:"ServiceUnavailableException",envelope:"http_envelope",httpStatus:503,retryable:true}),Object.freeze({wireCode:"TIER_UPGRADE_REQUIRED",envelope:"http_envelope",httpStatus:402,retryable:false}),Object.freeze({wireCode:"ThrottlingException",envelope:"http_envelope",httpStatus:429,retryable:true}),Object.freeze({wireCode:"ValidationException",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"accessDenied",envelope:"http_envelope",httpStatus:403,retryable:false}),Object.freeze({wireCode:"access_denied",envelope:"oauth_error",httpStatus:302,retryable:false}),Object.freeze({wireCode:"attachment_unreadable",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"authExpired",envelope:"http_envelope",httpStatus:401,retryable:true}),Object.freeze({wireCode:"authRejected",envelope:"http_envelope",httpStatus:401,retryable:false}),Object.freeze({wireCode:"consent_invalid",envelope:"http_envelope",httpStatus:422,retryable:false}),Object.freeze({wireCode:"consent_required",envelope:"http_envelope",httpStatus:422,retryable:false}),Object.freeze({wireCode:"consent_scope_wrong_route",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"device_cipher_unavailable",envelope:"http_envelope",httpStatus:503,retryable:true}),Object.freeze({wireCode:"email_invalid",envelope:"http_envelope",httpStatus:422,retryable:false}),Object.freeze({wireCode:"event_cursor_invalid",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"event_filter_invalid",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"event_payload_invalid",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"event_type_not_available",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"event_type_unknown",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"idempotency_key_invalid",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"idempotency_key_required",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"idempotency_key_reused",envelope:"http_envelope",httpStatus:409,retryable:false}),Object.freeze({wireCode:"insufficientCapability",envelope:"http_envelope",httpStatus:403,retryable:false}),Object.freeze({wireCode:"insufficient_scope",envelope:"http_envelope",httpStatus:403,retryable:false}),Object.freeze({wireCode:"internal_error",envelope:"sse_frame",httpStatus:200,retryable:false}),Object.freeze({wireCode:"invalidRequest",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"invalid_client",envelope:"oauth_error",httpStatus:401,retryable:false}),Object.freeze({wireCode:"invalid_client_metadata",envelope:"oauth_error",httpStatus:400,retryable:false}),Object.freeze({wireCode:"invalid_grant",envelope:"oauth_error",httpStatus:400,retryable:false}),Object.freeze({wireCode:"invalid_redirect_uri",envelope:"oauth_error",httpStatus:400,retryable:false}),Object.freeze({wireCode:"invalid_request",envelope:"oauth_error",httpStatus:400,retryable:false}),Object.freeze({wireCode:"invalid_scope",envelope:"oauth_error",httpStatus:400,retryable:false}),Object.freeze({wireCode:"invalid_target",envelope:"oauth_error",httpStatus:400,retryable:false}),Object.freeze({wireCode:"mcp_connection_not_found",envelope:"http_envelope",httpStatus:404,retryable:false}),Object.freeze({wireCode:"mcp_request_conflict",envelope:"http_envelope",httpStatus:409,retryable:false}),Object.freeze({wireCode:"mcp_request_gone",envelope:"http_envelope",httpStatus:410,retryable:false}),Object.freeze({wireCode:"mcp_request_not_found",envelope:"http_envelope",httpStatus:404,retryable:false}),Object.freeze({wireCode:"not_found",envelope:"sse_frame",httpStatus:200,retryable:false}),Object.freeze({wireCode:"partner_visit_link_conflict",envelope:"http_envelope",httpStatus:409,retryable:false}),Object.freeze({wireCode:"partner_visit_owner_only",envelope:"http_envelope",httpStatus:403,retryable:false}),Object.freeze({wireCode:"partner_visit_pet_already_mapped",envelope:"http_envelope",httpStatus:409,retryable:false}),Object.freeze({wireCode:"payload_too_large",envelope:"http_envelope",httpStatus:413,retryable:false}),Object.freeze({wireCode:"petRefInUse",envelope:"http_envelope",httpStatus:409,retryable:false}),Object.freeze({wireCode:"pet_health_list_removals_unconfirmed",envelope:"http_envelope",httpStatus:422,retryable:false}),Object.freeze({wireCode:"pet_health_species_unsupported",envelope:"http_envelope",httpStatus:422,retryable:false}),Object.freeze({wireCode:"pet_not_registered",envelope:"http_envelope",httpStatus:404,retryable:false}),Object.freeze({wireCode:"provider_unsupported",envelope:"http_envelope",httpStatus:422,retryable:false}),Object.freeze({wireCode:"rateLimited",envelope:"http_envelope",httpStatus:429,retryable:true}),Object.freeze({wireCode:"record_species_unsupported",envelope:"http_envelope",httpStatus:422,retryable:false}),Object.freeze({wireCode:"record_subject_not_found",envelope:"http_envelope",httpStatus:404,retryable:false}),Object.freeze({wireCode:"record_upload_invalid",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"records_consent_required",envelope:"http_envelope",httpStatus:409,retryable:false}),Object.freeze({wireCode:"sandbox_only",envelope:"http_envelope",httpStatus:403,retryable:false}),Object.freeze({wireCode:"session_required",envelope:"http_envelope",httpStatus:403,retryable:false}),Object.freeze({wireCode:"share_limit_reached",envelope:"http_envelope",httpStatus:429,retryable:false}),Object.freeze({wireCode:"slow_down",envelope:"oauth_error",httpStatus:429,retryable:true}),Object.freeze({wireCode:"species_unsupported",envelope:"http_envelope",httpStatus:422,retryable:false}),Object.freeze({wireCode:"tenant_filter_invalid",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"tenant_has_no_capabilities",envelope:"http_envelope",httpStatus:409,retryable:false}),Object.freeze({wireCode:"tenant_resource_not_found",envelope:"http_envelope",httpStatus:404,retryable:false}),Object.freeze({wireCode:"tenant_surface_not_enabled",envelope:"http_envelope",httpStatus:409,retryable:false}),Object.freeze({wireCode:"timeout",envelope:"sse_frame",httpStatus:200,retryable:true}),Object.freeze({wireCode:"token_invalid",envelope:"http_envelope",httpStatus:422,retryable:false}),Object.freeze({wireCode:"unavailable",envelope:"sse_frame",httpStatus:200,retryable:true}),Object.freeze({wireCode:"unavailable",envelope:"http_envelope",httpStatus:503,retryable:true}),Object.freeze({wireCode:"unsupported_grant_type",envelope:"oauth_error",httpStatus:400,retryable:false}),Object.freeze({wireCode:"unsupported_response_type",envelope:"oauth_error",httpStatus:302,retryable:false}),Object.freeze({wireCode:"verify_mint_rate_limited",envelope:"http_envelope",httpStatus:429,retryable:true}),Object.freeze({wireCode:"verify_mint_refused",envelope:"http_envelope",httpStatus:403,retryable:false}),Object.freeze({wireCode:"verify_mint_unavailable",envelope:"http_envelope",httpStatus:503,retryable:true}),Object.freeze({wireCode:"verify_user_ref_invalid",envelope:"http_envelope",httpStatus:422,retryable:false}),Object.freeze({wireCode:"webhook_endpoint_update_empty",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"webhook_endpoint_update_invalid",envelope:"http_envelope",httpStatus:400,retryable:false}),Object.freeze({wireCode:"webhook_url_rejected",envelope:"http_envelope",httpStatus:400,retryable:false})]);Object.freeze(re.map(e=>e.wireCode));var Br=Object.freeze({ValidationException:"validationFailed",AuthorizationException:"authRejected",PaymentRequiredException:"paymentRequired",AccessDeniedException:"accessDenied",ResourceNotFoundException:"resourceNotFound",ConflictException:"conflict",ThrottlingException:"rateLimited",InternalFailureException:"internalError",NotImplementedException:"notImplemented",ServiceUnavailableException:"serviceUnavailable",MethodNotAllowedException:"validationFailed",authExpired:"authExpired",unavailable:"serviceUnavailable",session_required:"sessionRequired",accessDenied:"accessDenied",invalidRequest:"sdkUpdateRequired",authRejected:"authRejected",rateLimited:"rateLimited",insufficientCapability:"accessDenied",event_cursor_invalid:"validationFailed",event_filter_invalid:"validationFailed",event_payload_invalid:"validationFailed",event_type_unknown:"validationFailed",idempotency_key_invalid:"validationFailed",idempotency_key_required:"validationFailed",idempotency_key_reused:"idempotencyConflict",payload_too_large:"validationFailed",webhook_url_rejected:"validationFailed",webhook_endpoint_update_empty:"validationFailed",webhook_endpoint_update_invalid:"validationFailed",event_type_not_available:"conflict",record_subject_not_found:"resourceNotFound",records_consent_required:"conflict",record_species_unsupported:"validationFailed",record_upload_invalid:"validationFailed",FILE_TOO_LARGE:"validationFailed",INVALID_CONTENT_TYPE:"validationFailed",attachment_unreadable:"validationFailed",provider_unsupported:"validationFailed",token_invalid:"validationFailed",device_cipher_unavailable:"serviceUnavailable",email_invalid:"validationFailed",consent_required:"validationFailed",consent_invalid:"validationFailed",consent_scope_wrong_route:"validationFailed",IDEMPOTENCY_KEY_IN_FLIGHT:"idempotencyInFlight",IDEMPOTENCY_KEY_REUSED_DIFFERENT_REQUEST:"idempotencyConflict",DAILY_QUOTA_EXCEEDED:"quotaExceeded",TIER_UPGRADE_REQUIRED:"paymentRequired",not_found:"conversationNotFound",timeout:"streamTimeout",internal_error:"internalError",invalid_request:"validationFailed",invalid_client:"authRejected",invalid_grant:"authRejected",unsupported_grant_type:"validationFailed",invalid_scope:"validationFailed",invalid_target:"validationFailed",invalid_redirect_uri:"validationFailed",invalid_client_metadata:"validationFailed",unsupported_response_type:"validationFailed",access_denied:"accessDenied",slow_down:"rateLimited",mcp_request_not_found:"resourceNotFound",mcp_request_gone:"resourceNotFound",mcp_connection_not_found:"resourceNotFound",mcp_request_conflict:"conflict",sandbox_only:"accessDenied",tenant_has_no_capabilities:"conflict",verify_mint_refused:"accessDenied",verify_mint_rate_limited:"rateLimited",verify_mint_unavailable:"serviceUnavailable",verify_user_ref_invalid:"validationFailed",insufficient_scope:"accessDenied",tenant_filter_invalid:"validationFailed",tenant_resource_not_found:"resourceNotFound",tenant_surface_not_enabled:"conflict",pet_health_species_unsupported:"validationFailed",pet_health_list_removals_unconfirmed:"validationFailed",petRefInUse:"conflict",share_limit_reached:"conflict",pet_not_registered:"resourceNotFound",species_unsupported:"validationFailed",partner_visit_owner_only:"accessDenied",partner_visit_link_conflict:"conflict",partner_visit_pet_already_mapped:"conflict"}),we=Object.freeze(re.map(e=>Object.freeze({wireCode:e.wireCode,envelope:e.envelope,retryable:e.retryable,normalized:Br[e.wireCode]}))),Vr=new Map(we.map(e=>[e.envelope+e.wireCode.toUpperCase(),e])),qr=Object.freeze(["http_envelope",...[...new Set(we.map(e=>e.envelope))].filter(e=>e!=="http_envelope").sort()]);function V(e,r){if(typeof e!="string")return;let t=e.trim().toUpperCase();if(t.length!==0)for(let n of [r??"http_envelope",...qr]){let i=Vr.get(n+t);if(i)return i}}o(V,"contractErrorFor");function Hr(e){return e.envelope==="sse_frame"?`/errors#sse-${e.wireCode}`:e.envelope==="oauth_error"?`/errors#oauth-${e.wireCode}`:`/errors#${e.wireCode}`}o(Hr,"contractDocUrl");function Fe(e){return null}o(Fe,"publishedDocUrl");var Kr=Object.freeze({authExpired:"authentication",authRejected:"authentication",sessionRequired:"authentication",accessDenied:"authorization",paymentRequired:"entitlement",quotaExceeded:"rateLimit",rateLimited:"rateLimit",validationFailed:"invalidRequest",sdkUpdateRequired:"invalidRequest",conversationNotFound:"notFound",petNotFound:"notFound",resourceNotFound:"notFound",conflict:"conflict",idempotencyConflict:"conflict",idempotencyInFlight:"conflict",streamTimeout:"connection",streamIncomplete:"connection",upstreamLlmTimeout:"connection",serviceUnavailable:"connection",internalError:"server",notImplemented:"server",mediaUploadFailed:"media",moderationBlocked:"policy",unknown:"unknown"});function Oe(e){return Kr[e]}o(Oe,"categoryFor");function Pe(e){let r=V(e);if(r)return r.normalized;let t=(e??"").trim().toUpperCase(),n=t.replace(/[^A-Z0-9]/g,"");if(n==="AUTHREJECTED")return "authRejected";if(t.startsWith("AUTH"))return "authExpired";switch(n){case "DAILYQUOTAEXCEEDED":case "QUOTAEXCEEDED":return "quotaExceeded";case "RATELIMITED":case "TOOMANYREQUESTS":return "rateLimited";case "IDEMPOTENCYKEYCONFLICT":case "IDEMPOTENCYCONFLICT":return "idempotencyConflict";case "ACCESSDENIED":case "FORBIDDEN":return "accessDenied";case "NOTFOUND":case "CONVERSATIONNOTFOUND":return "conversationNotFound";case "PETNOTFOUND":return "petNotFound";case "TIMEOUT":case "STREAMTIMEOUT":return "streamTimeout";case "MEDIAUPLOADFAILED":case "UNRECOGNIZEDVIDEOFORMAT":case "FILETOOSMALL":return "mediaUploadFailed";case "MODERATIONBLOCKED":return "moderationBlocked";case "UPSTREAMLLMTIMEOUT":return "upstreamLlmTimeout";case "INTERNALERROR":case "INTERNALSERVERERROR":return "internalError";case "STREAMINCOMPLETE":case "CHATSTREAMINCOMPLETE":return "streamIncomplete";case "INVALIDANALYSISTYPE":case "INVALIDUPLOADPURPOSE":return "unknown";default:return "unknown"}}o(Pe,"fromWire");var Gr=new Set(["streamTimeout","rateLimited","internalError","upstreamLlmTimeout","streamIncomplete"]);function te(e){return Gr.has(e)}o(te,"isAutoRetryable");var Yr=new Set(["serviceUnavailable","conflict","idempotencyInFlight"]);function Ie(e){return te(e)||Yr.has(e)}o(Ie,"isRetryableCode");var Wr=500,ne=8e3,Ae=4;function ze(e,r=Math.random){let t=Math.max(0,e),n=Math.min(Wr*2**t,ne);return Math.floor(r()*n)}o(ze,"backoffDelayMs");var Jr=Object.freeze({authExpired:"Please sign in again to continue.",authRejected:"Please sign in again to continue.",sessionRequired:"This app needs to be updated to keep using Everfur.",accessDenied:"This feature isn't available on your account right now.",paymentRequired:"This is part of a paid plan.",quotaExceeded:"You have used today's messages. This resets tomorrow.",rateLimited:"You are sending messages quickly. Try again in a few seconds.",validationFailed:"Something in that request was not right. Check the details and try again.",sdkUpdateRequired:"This app needs to be updated to keep using Everfur.",conflict:"That is already running. Wait for it to finish, then try again.",idempotencyConflict:"That message was already delivered.",idempotencyInFlight:"That is still processing. Give it a moment.",conversationNotFound:"This conversation is no longer available.",petNotFound:"We could not find that pet. Please pick a pet and try again.",resourceNotFound:"We could not find that.",streamTimeout:"The response timed out. Tap retry to try again.",mediaUploadFailed:"We could not upload that photo. Please attach it again.",moderationBlocked:"This request cannot be answered here.",upstreamLlmTimeout:"The response timed out. Tap retry to try again.",internalError:"We hit a snag on our end. Please try again.",notImplemented:"That is not available yet.",serviceUnavailable:"That service is briefly unavailable. Try again shortly.",streamIncomplete:"The connection dropped mid-answer. Tap retry to continue.",unknown:"Something went wrong. Please try again."});function oe(e){return Jr[e]}o(oe,"userFacingError");function je(e,r,t){Object.defineProperty(e,r,{value:t,enumerable:false,writable:false,configurable:false});}o(je,"defineHidden");var $r=/^[a-z][a-z0-9_]{0,63}$/;function m(e,r){let t=V(r?.wireCode,r?.wireEnvelope),n={code:e,category:Oe(e),retryable:r?.retryable??Ie(e),displayMessage:oe(e)};if(r?.httpStatus!==void 0&&(n.httpStatus=r.httpStatus),r?.requestId&&(n.requestId=r.requestId),typeof r?.reasonCode=="string"&&$r.test(r.reasonCode)&&(n.reasonCode=r.reasonCode),t);return r?.wireMessage&&je(n,"wireMessage",r.wireMessage),r?.raw!==void 0&&r.raw!==null&&je(n,"raw",r.raw),Object.freeze(n)}o(m,"makeEverfurError");function j(e,r){return Object.freeze(r===void 0?{ok:true,value:e}:{ok:true,value:e,warning:r})}o(j,"ok");function y(e){return Object.freeze({ok:false,error:e})}o(y,"err");var q=class q extends Error{constructor(r,t){super(t),this.name="EverfurConfigError",this.kind=r,Object.setPrototypeOf(this,q.prototype);}};o(q,"EverfurConfigError");var z=q;function H(e){throw new Error(`assertNever: unhandled discriminated-union member ${JSON.stringify(e)}`)}o(H,"assertNever");function De(e){return Object.freeze({kind:"error",terminal:true,code:e.code,retryable:e.retryable,wireMessage:e.wireMessage,synthesized:e.synthesized,lastEventId:e.lastEventId,serverRequestId:e.serverRequestId??null})}o(De,"errorFrame");function Zr(e){let r={};return e.mode==="personalized"?r.authorization=`Bearer ${e.token}`:r["x-everfur-partner-key"]=e.publishableKey,e.userRef&&(r["x-everfur-user-ref"]=e.userRef),e.sessionId&&(r["x-everfur-session-id"]=e.sessionId),r}o(Zr,"authHeaders");function D(e,r){return {...e,headers:{...et(e.headers),...Zr(r)}}}o(D,"withAuthHeaders");function et(e){if(!e)return {};let r={};for(let[t,n]of Object.entries(e)){let i=t.toLowerCase();i==="authorization"||i==="x-everfur-partner-key"||(r[t]=n);}return r}o(et,"stripAuthHeaders");async function Ne(e,r){if(r.mode==="anonymous")return r.transport.request(D(e,{mode:"anonymous",publishableKey:r.publishableKey,userRef:r.userRef,...r.sessionId?{sessionId:r.sessionId}:{}}));let t;try{t=await r.identity.currentToken();}catch{return y(ie())}let n=await r.transport.request(D(e,{mode:"personalized",token:t,userRef:r.userRef,...r.sessionId?{sessionId:r.sessionId}:{}}));if(n.ok||n.error.httpStatus!==401)return n;let i;try{i=await r.identity.currentToken({forceRefresh:!0});}catch{return y(ie())}let a=await r.transport.request(D(e,{mode:"personalized",token:i,userRef:r.userRef,...r.sessionId?{sessionId:r.sessionId}:{}}));return !a.ok&&a.error.httpStatus===401?y(ie(a.error.httpStatus)):a}o(Ne,"authedSend");function ie(e){return m("authRejected",{retryable:false,...e!==void 0?{httpStatus:e}:{}})}o(ie,"authRejectedError");function Me(e){return m("internalError",{retryable:true,wireMessage:rt(e)})}o(Me,"networkError");function rt(e){return e instanceof Error?e.message:typeof e=="string"?e:"network request failed"}o(rt,"describeCause");var tt=3e4,nt=ne*2,ot="idempotency-key",it=new Set(["POST","PUT","PATCH","DELETE"]),at=Object.freeze({onEvent(){}}),st=Object.freeze({setTimeout:o((e,r)=>setTimeout(e,r),"setTimeout"),clearTimeout:o(e=>clearTimeout(e),"clearTimeout"),now:o(()=>Date.now(),"now")});function lt(e){let r=e.trim().replace(/[^A-Za-z0-9_.:-]/g,"-");return r.length===0?null:`efs_${r}`.slice(0,128)}o(lt,"seededIdempotencyKey");function Ue(e){let r=e.observer??at,t=e.timers??st,n=e.rng??Math.random,i=e.sleep??((h,g)=>_t(h,t,g)),a=e.maxAttempts??Ae,s=e.unaryTimeoutMs??tt,l=e.newIdempotencyKey??ut(),u=o(h=>{try{r.onEvent(h);}catch{}},"emit");async function p(h,g,v){let k=v?.maxAttempts,w=Math.max(1,k??a),Nr=v?.idempotent??it.has(g.method),Mr=v?.idempotencyKey===void 0?null:lt(v.idempotencyKey),Q=Nr?Mr??l():null,Lr=dt(g,Q),X=0;for(;;){let I=o(ee=>Object.freeze({call:h,method:g.method,path:g.path,attempt:X,...Q?{idempotencyKey:Q}:{},...ee}),"stamp");u(I({phase:"attempt"}));let Te=ht(s,t),ke=Et(Te.signal,g.signal),A;try{A=await Ne({...Lr,signal:ke.signal},e.auth);}catch(ee){A=y(Me(ee));}finally{ke.dispose(),Te.cancel();}if(A.ok)return u(I({phase:"settled",ok:true,reason:"ok"})),A;let T=A.error,B=pt(T,X,{maxAttempts:w,rng:n,now:t.now(),aborted:g.signal?.aborted===true,...k!==void 0?{perCallMaxAttempts:k}:{},...e.errorPolicy!==void 0?{policy:e.errorPolicy}:{}}),Z={ok:false,code:T.code,reason:B.reason,...T.httpStatus!==void 0?{httpStatus:T.httpStatus}:{},...T.requestId!==void 0?{requestId:T.requestId}:{}};if(!B.retry)return u(I({phase:"settled",...Z})),y(T);if(u(I({phase:"retry",...Z,delayMs:B.delayMs})),await i(B.delayMs,g.signal),g.signal?.aborted===true)return u(I({phase:"settled",...Z,reason:"aborted"})),y(T);X+=1;}}o(p,"send");async function f(h){return D(h,await vt(e.auth))}o(f,"authorize");async function*R(h,g,v){u(Object.freeze({call:h,phase:"stream.open",method:g.method,path:g.path,attempt:0}));try{yield*e.auth.transport.stream(g,v);}catch{yield De({code:"internal_error",retryable:true,wireMessage:null,synthesized:true,lastEventId:null,serverRequestId:null});}}return o(R,"stream"),Object.freeze({send:p,authorize:f,stream:R})}o(Ue,"createRequestFunnel");function dt(e,r){let t={};for(let[n,i]of Object.entries(e.headers??{}))n.toLowerCase()!==ot&&(t[n]=i);return r!==null&&(t["Idempotency-Key"]=r),{...e,headers:t}}o(dt,"assembleHeaders");function ut(){let e=0;return ()=>`efk_${Date.now().toString(36)}_${(e++).toString(36)}_${Math.random().toString(36).slice(2,12)}`}o(ut,"defaultIdempotencyKeyFactory");var ct=10;function pt(e,r,t){if(t.aborted)return {retry:false,reason:"aborted"};let n=ft(e);if(n==="quota")return {retry:false,reason:"daily-quota-exhausted"};if(n!=="burst"&&!te(e.code))return {retry:false,reason:"terminal-code"};let i=n==="burst"?"burst-throttle":"retryable-code";if(o(()=>{if(t.policy===void 0)return false;try{return !t.policy.shouldRetry(e.code,r)}catch{return false}},"policyVetoedRetry")())return {retry:false,reason:"terminal-code"};let s=o(()=>{if(t.policy===void 0)return null;let p;try{p=t.policy.maxAttempts(e.code);}catch{return null}return typeof p!="number"||!Number.isFinite(p)?null:Math.max(1,Math.min(ct,Math.floor(p)))},"policyCeiling"),l=t.perCallMaxAttempts??s()??t.maxAttempts;if(r+1>=l)return {retry:false,reason:"attempts-exhausted"};let u=yt(e,t.now);return u!==null?u>nt?{retry:false,reason:"server-reset-beyond-budget"}:{retry:true,reason:i,delayMs:u}:{retry:true,reason:i,delayMs:ze(r,t.rng)}}o(pt,"decideRetry");function ft(e){let r=e.code==="unknown"?Pe(gt(e.raw)):e.code;return r==="quotaExceeded"?"quota":r==="rateLimited"||e.httpStatus===429?"burst":null}o(ft,"throttleKind");function yt(e,r){let t=mt(e.raw);if(t===null)return null;let n=t-r;return n>0?n:0}o(yt,"retryOverrideMs");function mt(e){for(let r of Be(e)){let t=r.resets_at,n=bt(t);if(n!==null)return n}return null}o(mt,"findResetsAt");function Be(e){if(!Le(e))return [];let r=[e];for(let t of ["error","detail"]){let n=e[t];Le(n)&&r.push(n);}return r}o(Be,"candidateScopes");function bt(e){if(typeof e=="number"&&Number.isFinite(e))return e<1e11?Math.round(e*1e3):Math.round(e);if(typeof e=="string"&&e.trim().length>0){let r=Date.parse(e);return Number.isFinite(r)?r:null}return null}o(bt,"parseInstantMs");function gt(e){for(let r of Be(e)){let t=r.code;if(typeof t=="string"&&t.length>0)return t}return null}o(gt,"rawWireCode");function Le(e){return typeof e=="object"&&e!==null}o(Le,"isRecord");async function vt(e){return e.mode==="anonymous"?{mode:"anonymous",publishableKey:e.publishableKey,userRef:e.userRef,...e.sessionId?{sessionId:e.sessionId}:{}}:{mode:"personalized",token:await e.identity.currentToken(),userRef:e.userRef,...e.sessionId?{sessionId:e.sessionId}:{}}}o(vt,"resolveDeviceAuth");function ht(e,r){let t=new AbortController,n=r.setTimeout(()=>t.abort(),e);return {signal:t.signal,cancel:o(()=>r.clearTimeout(n),"cancel")}}o(ht,"boundedSignal");function Et(e,r){let t=o(()=>{},"noop");if(!r)return {signal:e,dispose:t};let n=new AbortController;if(e.aborted||r.aborted)return n.abort(),{signal:n.signal,dispose:t};let i=o(()=>n.abort(),"onAbort");return e.addEventListener("abort",i,{once:true}),r.addEventListener("abort",i,{once:true}),{signal:n.signal,dispose:o(()=>{e.removeEventListener("abort",i),r.removeEventListener("abort",i);},"dispose")}}o(Et,"mergeSignals");function _t(e,r,t){return e<=0||t?.aborted===true?Promise.resolve():new Promise(n=>{let i=r.setTimeout(()=>{t?.removeEventListener("abort",a),n();},e),a=o(()=>{r.clearTimeout(i),n();},"onAbort");t?.addEventListener("abort",a,{once:true});})}o(_t,"delayAbortable");var Ve=Object.freeze({track(){},trackExpectedFailure(){}});function qe(e){let r={uri:e.uri,mimeType:e.mimeType},t=G(e);return t!==null&&(r.sizeBytes=t),Object.freeze(r)}o(qe,"fileHandleFromMedia");function G(e){let r=e.byteSize??e.sizeBytes;return typeof r=="number"&&Number.isFinite(r)&&r>=0?r:null}o(G,"mediaByteSize");function He(e){let r=e.durationSeconds;return typeof r=="number"&&Number.isFinite(r)&&r>=0?Math.round(r*1e3):null}o(He,"mediaDurationMs");function Ke(e,r){return Object.freeze(r===void 0?{mode:e}:{mode:e,maxDurationSeconds:r})}o(Ke,"captureOptionsFor");function ae(e){return e!=null&&e.isAvailable===true}o(ae,"isCaptureAvailable");var Ge=Object.freeze({setTimeout:o((e,r)=>setTimeout(e,r),"setTimeout"),clearTimeout:o(e=>clearTimeout(e),"clearTimeout"),now:o(()=>Date.now(),"now")});function Ye(e,r,t){return new Promise(n=>{if(r.aborted){n();return}let i={handle:void 0},a=o(()=>{t.clearTimeout(i.handle),r.removeEventListener("abort",a),n();},"done");i.handle=t.setTimeout(a,e),r.addEventListener("abort",a,{once:true});})}o(Ye,"delayAbortable");function Rt(e,r){if(!r)return e;let t=new AbortController;if(e.aborted||r.aborted)return t.abort(),t.signal;let n=o(()=>t.abort(),"onAbort");return e.addEventListener("abort",n,{once:true}),r.addEventListener("abort",n,{once:true}),t.signal}o(Rt,"mergeSignals");var se=class se{constructor(r){this.listeners=new Set;this.epoch=0;this.inFlight=null;this.snapshot=Object.freeze({...r});}subscribe(r){return this.listeners.add(r),()=>{this.listeners.delete(r);}}getSnapshot(){return this.snapshot}patch(r){this.snapshot=Object.freeze({...this.snapshot,...r});for(let t of this.listeners)t();}beginOperation(r){this.inFlight?.abort();let t=new AbortController;this.inFlight=t;let n=this.epoch;return {signal:r?Rt(t.signal,r):t.signal,epoch:n,settle:o(()=>{this.inFlight===t&&(this.inFlight=null);},"settle")}}alive(r){return r===this.epoch}rescope(){this.epoch+=1,this.inFlight?.abort(),this.inFlight=null;}dispose(){this.epoch+=1,this.inFlight?.abort(),this.inFlight=null,this.listeners.clear();}};o(se,"MediaStore");var K=se;function We(e){for(let r=0;r<e.length;r+=1){let t=e.charCodeAt(r);if(t>=55296&&t<=56319){let n=e.charCodeAt(r+1);if(!(n>=56320&&n<=57343))return true;r+=1;}else if(t>=56320&&t<=57343)return true}return false}o(We,"hasLoneSurrogate");var St=Object.freeze(["primary_label","primary_confidence","subtype_label","subtype_display","subtype_confidence","risk_tier","confidence_tier","copy_short","disease_name","disease_framing","model_version","skin_v2","eye_v2","llm_checkup"]),Ct=Object.freeze(["decision_audit","decision_thresholds","per_class_auroc","ood_layers","gate1_score","possible_conditions","top_class"]),Tt=new Set(["skin_v2","eye_v2","llm_checkup"]),kt=new Set(["primary_confidence","subtype_confidence"]),xt=new Set(["low_risk","concern","uncertain"]),wt=new Set(["possible","probable"]),Ft=Object.freeze({primary_label:64,subtype_label:64,subtype_display:128,copy_short:512,disease_name:128,disease_framing:512,model_version:64});function Je(e){let r=c(e)?e:{},t={};for(let i of St){if(!(i in r))continue;let a=r[i],s=Ot(i,a);s!==F&&(t[i]=s);}let n=0;for(let i of Ct)i in r&&(n+=1);return Object.freeze({metadata:Object.freeze(t),droppedOperatorKeys:n})}o(Je,"projectDetectionMetadata");var F=Symbol("drop");function Ot(e,r){if(Tt.has(e))return typeof r=="boolean"?r:F;if(kt.has(e))return $e(r)?r:F;if(e==="risk_tier")return typeof r=="string"&&xt.has(r)?r:F;if(e==="confidence_tier")return typeof r=="string"&&wt.has(r)?r:F;if(typeof r!="string")return F;let t=Ft[e]??256;return r.length>t?r.slice(0,t):r}o(Ot,"acceptKey");function $e(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0&&e<=1}o($e,"isUnitConfidence");function Qe(e){if(!c(e))return null;let r=b(e.summary),t=b(e.disclaimer),n=le(e.recommendations??e.recommendation);return r===null&&t===null&&n.length===0?null:Object.freeze({summary:r??"",recommendations:Object.freeze(n),disclaimer:t??""})}o(Qe,"parseInterpretation");function Xe(e){if(!c(e))return {};if(c(e.metadata))return e.metadata;let r=e.detections;if(Array.isArray(r)&&r.length>0){let t=r[0];if(c(t)&&c(t.metadata))return t.metadata;if(c(t))return t}return e}o(Xe,"locateDetectionMetadata");function Ze(e){return c(e)?c(e.interpretation)?e.interpretation:e:null}o(Ze,"locateInterpretation");function c(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}o(c,"isRecord");function b(e){return typeof e=="string"&&e.length>0?e:null}o(b,"asString");function le(e){return typeof e=="string"?e.length>0?[e]:[]:Array.isArray(e)?e.filter(r=>typeof r=="string"&&r.length>0):[]}o(le,"asStringList");function er(e){return $e(e)?e:null}o(er,"asUnitNumber");function O(e){return typeof e=="number"&&Number.isFinite(e)?e:null}o(O,"asFiniteNumber");var Y=Object.freeze({initiate:"/widget/v1/uploads/initiate",precheck:"/widget/v1/photo/precheck",photoAnalyze:"/widget/v1/photo/analyze",videoAnalyze:"/widget/v1/video/analyze",job:o(e=>We(e)?null:`/widget/v1/analysis/jobs/${encodeURIComponent(e)}`,"job")});async function tr(e,r){let t={purpose:r.purpose,content_type:r.contentType};r.byteSize!==null&&(t.byte_size=r.byteSize);let n={method:"POST",path:Y.initiate,body:t,...r.signal?{signal:r.signal}:{}},i=await e.send("media.initiateUpload",n);if(!i.ok)return y(i.error);let a=Pt(i.value);return a===null?y(m("mediaUploadFailed",{retryable:false})):j(a)}o(tr,"initiateUpload");function Pt(e){if(!c(e))return null;let r=b(e.url),t=b(e.s3_key);if(r===null||t===null)return null;let n=e.fields,i={};if(c(n))for(let[a,s]of Object.entries(n))typeof s=="string"&&(i[a]=s);return Object.freeze({url:r,fields:Object.freeze(i),s3Key:t,expiresAt:O(e.expires_at)})}o(Pt,"parsePresigned");async function nr(e,r,t,n,i){try{return await e.presignedPut(qe(r),{target:t,idempotencyKey:n,...i?{signal:i}:{}}),j(void 0)}catch{return y(m("mediaUploadFailed",{retryable:false}))}}o(nr,"stageUpload");async function or(e,r,t,n,i){let a={method:"POST",path:r,body:t,...i?{signal:i}:{}},s=await e.send("media.analyze",a);if(s.ok){let u=It(s.value);return u!==null?{kind:"async",job:u}:{kind:"sync",body:s.value}}let l=n(s.error.httpStatus,s.error);return l!==null?{kind:"gate",gate:l}:{kind:"error",error:s.error}}o(or,"submitAnalyze");function It(e){if(!c(e))return null;let r=b(e.job_id);if(r===null)return null;let t=b(e.status);return t!==null&&Dt(t)?null:Object.freeze({jobId:r,estimatedSeconds:O(e.estimated_completion_seconds)})}o(It,"parseAsyncJob");var At=2e3,rr=5e3,zt=9e4;async function ir(e,r,t){let{timers:n,signal:i,alive:a}=t,s=n.now(),l=Math.max(zt,(t.estimatedSeconds??0)*1e3),u=At;for(;;){if(i.aborted||!a())return {kind:"aborted"};if(await Ye(u,i,n),i.aborted||!a())return {kind:"aborted"};if(n.now()-s>l)return {kind:"timeout"};let p=Y.job(r);if(p===null)return {kind:"notFound"};let f=await e.send("media.pollJob",{method:"GET",path:p,...i?{signal:i}:{}},{maxAttempts:1});if(i.aborted||!a())return {kind:"aborted"};if(!f.ok){if(f.error.httpStatus===404)return {kind:"notFound"};u=Math.min(rr,u+1e3);continue}let R=jt(f.value);if(R==="COMPLETED")return {kind:"complete",body:f.value};if(R==="FAILED")return {kind:"failed",error:Nt(f.value)};u=Math.min(rr,u+1e3);}}o(ir,"pollJob");function jt(e){return c(e)?(b(e.status)??"PROCESSING").toUpperCase():"PROCESSING"}o(jt,"jobStatusOf");function Dt(e){let r=e.toUpperCase();return r==="COMPLETED"||r==="FAILED"}o(Dt,"isTerminalStatus");function Nt(e){let r=c(e)?b(e.error)??b(e.error_code):null;return m("internalError",{retryable:false,...r?{wireMessage:r}:{}})}o(Nt,"jobFailureError");function ar(){return null}o(ar,"detectNoGate");function sr(e,r){let t={s3_key:e};return r.type!==void 0&&(t.type=r.type),r.petRef!=null&&(t.pet_id=r.petRef),r.precheckToken!=null&&(t.precheck_token=r.precheckToken),t}o(sr,"analyzeBody");function lr(e){return new de(e)}o(lr,"createVideoController");var ue=class ue extends K{constructor(r){super({status:"idle",job:null,result:null,error:null,isCaptureAvailable:ae(r.camera),progress:0}),this.funnel=r.funnel??Ue({auth:r.auth,...r.errorPolicy?{errorPolicy:r.errorPolicy}:{}}),this.camera=r.camera,this.upload=r.upload,this.defaultPetRef=r.petRef??null,this.telemetry=r.telemetry??Ve,this.timers=r.timers??Ge,this.makeIdempotencyKey=r.makeIdempotencyKey??Vt();}async capture(r){if(!ae(this.camera))return y(m("unknown",{retryable:false}));let t;try{t=await this.camera.capture(Ke("video",15));}catch{return this.snapshot.status!=="idle"&&this.patch({status:"idle",progress:0}),y(m("unknown",{retryable:false}))}return this.submit(t,r?.petRef!==void 0?{petRef:r.petRef}:void 0)}async submit(r,t){let n=this.beginOperation(t?.signal),i=t?.petRef??this.defaultPetRef,a=this.makeIdempotencyKey(),s=this.timers.now();this.patch({status:"uploading",progress:0,error:null,result:null,job:null});try{let l=await tr(this.funnel,{purpose:"gait_video",contentType:r.mimeType,byteSize:G(r),signal:n.signal});if(n.signal.aborted||!this.alive(n.epoch))return x();if(!l.ok)return this.fail(n,l.error);let u=await nr(this.upload,r,l.value,a,n.signal);if(n.signal.aborted||!this.alive(n.epoch))return x();if(!u.ok)return this.telemetry.track("analysis.upload",{capability:"video",purpose:"gait_video",outcome:"failed"}),this.fail(n,u.error);this.patch({progress:1}),this.telemetry.track("analysis.upload",{capability:"video",purpose:"gait_video",byteSize:G(r)??0,durationMs:He(r)??0,outcome:"ok"}),this.patch({status:"analyzing"});let p=await or(this.funnel,Y.videoAnalyze,sr(l.value.s3Key,{petRef:i}),ar,n.signal);if(n.signal.aborted||!this.alive(n.epoch))return x();if(p.kind==="error")return this.fail(n,p.error);if(p.kind==="gate")return this.fail(n,m("unknown",{retryable:!1}));if(p.kind==="sync"){let h=Bt(p.body)??"";return this.complete(n,p.body,h,s)}let f=p.job;this.patch({job:{id:f.jobId,state:"processing"}});let R=await ir(this.funnel,f.jobId,{timers:this.timers,signal:n.signal,alive:o(()=>this.alive(n.epoch),"alive"),estimatedSeconds:f.estimatedSeconds});if(n.signal.aborted||!this.alive(n.epoch))return x();switch(R.kind){case "aborted":return x();case "complete":return this.complete(n,R.body,f.jobId,s);case "failed":return this.fail(n,R.error);case "timeout":return this.fail(n,m("streamTimeout",{retryable:!0}));case "notFound":return this.patch({job:null}),this.fail(n,m("conversationNotFound",{retryable:!1,httpStatus:404}));default:return this.fail(n,m("internalError",{retryable:!1}))}}finally{n.settle();}}cancel(){this.inFlight?.abort(),this.inFlight=null,(this.snapshot.status==="uploading"||this.snapshot.status==="analyzing")&&this.patch({status:"idle",progress:0});}complete(r,t,n,i){if(!this.alive(r.epoch))return x();let a=Mt(t,n);return a.droppedOperatorKeys>0&&this.telemetry.track("analysis.metadata_leak_guard",{droppedKeyCount:a.droppedOperatorKeys}),this.telemetry.track("analysis.quality_gate",{usable:a.result.quality.usable,reasons:a.result.quality.reasons}),this.patch({status:"complete",progress:1,result:a.result,error:null,job:{id:n,state:"complete"}}),this.telemetry.track("analysis.result",{capability:"video",modelVersion:a.result.modelVersion??"",timeToResultMs:this.timers.now()-i}),j(n)}fail(r,t){return this.alive(r.epoch)?(this.patch({status:"error",error:t,progress:0}),this.telemetry.trackExpectedFailure("analysis.error",{code:t.code,...t.httpStatus!==void 0?{httpStatus:t.httpStatus}:{}}),y(t)):x()}};o(ue,"VideoController");var de=ue;function Mt(e,r){let t=Je(Xe(e)),n=Qe(Ze(e)),i=b(t.metadata.model_version)??(c(e)?b(e.model_version):null);return {result:Object.freeze({jobId:r,analysisType:"gait",detections:Lt(e),interpretation:n,modelVersion:i,quality:Ut(e)}),droppedOperatorKeys:t.droppedOperatorKeys}}o(Mt,"mapVideoResult");function Lt(e){let r=c(e)?e.detections:null;if(!Array.isArray(r))return Object.freeze([]);let t=[];for(let n of r){if(!c(n))continue;let i=b(n.label)??b(n.name);i!==null&&t.push(Object.freeze({label:i,confidence:er(n.confidence)??0,timestampMs:O(n.timestamp_ms??n.timestamp),durationMs:O(n.duration_ms??n.duration)}));}return Object.freeze(t)}o(Lt,"parseVideoDetections");function Ut(e){let r=c(e)?e.quality:null;if(!c(r))return Object.freeze({usable:true,reasons:Object.freeze([])});let t=r.usable;return Object.freeze({usable:typeof t=="boolean"?t:true,reasons:Object.freeze(le(r.reasons))})}o(Ut,"parseQuality");function Bt(e){return c(e)?b(e.result_id)??b(e.request_id)??b(e.job_id):null}o(Bt,"resultIdOf");function x(){return y(m("unknown",{retryable:false}))}o(x,"abortedResult");function Vt(){let e=0;return ()=>`video-analyze-${Date.now().toString(36)}-${(e++).toString(36)}-${Math.random().toString(36).slice(2,10)}`}o(Vt,"defaultIdempotencyFactory");var dr=Symbol.for("@everfur/sdk#EverfurContext@v1"),ur=globalThis,cr=ur[dr]??(ur[dr]=react.createContext(null));cr.displayName??="EverfurContext";function W(){let e=react.useContext(cr);if(e===null)throw new z("not-initialized","Everfur hooks must be called inside <EverfurProvider>. Wrap your tree once, high.");return e}o(W,"useEverfurContext");var Jt=Object.freeze({status:"idle",job:null,result:null,error:null,isCaptureAvailable:false,progress:0}),$t=o(()=>{},"NOOP_UNSUBSCRIBE");function ce(e){let{runtime:r,userRef:t,activePet:n,scopeEpoch:i}=W(),a=e?.petRef??n??null,[s,l]=react.useState(null);react.useEffect(()=>{let{services:v}=r.registry,k=r.registry.acquire("video",`${t??""}|${a??""}`,()=>lr({...v.errorPolicy?{errorPolicy:v.errorPolicy}:{},auth:v.authFor(t),camera:v.camera,upload:v.upload,petRef:a,telemetry:v.telemetry}),w=>w.dispose());return l(k),()=>{l(w=>w===k?null:w),r.registry.release(k);}},[r,t,a,i]);let u=react.useCallback(v=>s?s.subscribe(v):$t,[s]),p=react.useCallback(()=>s?s.getSnapshot():Jt,[s]),f=react.useSyncExternalStore(u,p,p),R=react.useCallback(v=>s?s.submit(v):Promise.resolve(pr()),[s]),h=react.useCallback(()=>s?s.capture():Promise.resolve(pr()),[s]),g=react.useCallback(()=>{s?.cancel();},[s]);return react.useMemo(()=>({status:f.status,job:f.job,result:f.result,error:f.error,isCaptureAvailable:f.isCaptureAvailable,progress:f.progress,isBootstrapping:s===null,submit:R,capture:h,cancel:g}),[f,s,R,h,g])}o(ce,"useEverfurVideo");function pr(){return y(m("internalError",{retryable:true}))}o(pr,"notReadyError");function ye(e){if(e!==null&&typeof e=="object"&&!Object.isFrozen(e)){Object.freeze(e);for(let r of Object.keys(e))ye(e[r]);}return e}o(ye,"deepFreeze");var Qt=Object.freeze({content:0,heading:2,control:1.8,compact:1.5,badge:1.3}),yr={xs:4,sm:8,md:12,lg:16,xl:24,xxl:32},mr={tile:5,xs:6,sm:10,md:14,lg:20,xl:28,pill:500},fr={nano:8,small:9,micro:10,tiny:11,caption:12,bodyTight:13,body:14,subtitle:16,heading:18,verdict:24},Xt={tight:1.15,snug:1.3,normal:1.4,relaxed:1.55},Zt={regular:"400",medium:"500",bold:"700"},en={eyebrow:1,monoWide:.9,headerBrand:-0.42,tight:-0.35},rn=0,J="System",$="monospace",tn=["content","heading","control","compact","badge"];function nn(e){let r={};for(let t of tn)r[t]=e;return r}o(nn,"authoredTierSizes");function br(){return {family:J,mono:$,fontUrl:null,families:{regular:J,medium:J,bold:J,mono:$,monoMedium:$,monoBold:$},namedFaces:{regular:false,medium:false,bold:false,mono:false,monoMedium:false,monoBold:false},fontUrls:{},size:fr,tierSizes:nn(fr),lineHeight:Xt,weight:Zt,letterSpacing:en,scale:1,maxScale:rn,scaleCaps:Qt}}o(br,"baseType");var gr={},pe=Object.freeze({standard:Object.freeze([.2,0,0,1]),emphasized:Object.freeze([.2,.8,.2,1]),exit:Object.freeze([.4,0,1,1])});function fe(e){return `cubic-bezier(${e.join(", ")})`}o(fe,"cssCubicBezier");var vr={duration:{instant:80,fast:140,normal:220,slow:320},spring:{gentle:{damping:18,stiffness:200}},easing:{standard:fe(pe.standard),emphasized:fe(pe.emphasized),exit:fe(pe.exit)},reducedMotion:false},on={brand:"#10462E",brandSoft:"rgba(16,70,46,0.10)",brandSofter:"rgba(16,70,46,0.15)",brandBorder:"rgba(16,70,46,0.18)",borderHairlineGreen:"rgba(16,70,46,0.25)",brandBorderStrong:"rgba(16,70,46,0.55)",brandInk:"#10462E",linkInk:"#10462E",surface:"#FFFFFF",surfaceCream:"#FDFBF8",surfaceSubtle:"#EFE9E3",background:"#FFFFFF",borderRingCream:"#D8D2C8",chatGround:"#FFFFFF",composerFill:"#FDFBF8",composerBorder:"rgba(16,70,46,0.75)",shadow:"#0B3421",text:{primary:"#111111",secondary:"#0E462E",muted:"#6E6660",placeholder:"rgba(61,61,61,0.75)",onBrand:"#FFFFFF",metaCool:"#3D3D3D",timestamp:"rgba(61,61,61,0.75)"},primary:"#10462E",onPrimary:"#FFFFFF",accent:"#10462E",onAccent:"#FFFFFF",accentEdge:"#07C168",success:"#07C168",danger:"#B91C1C",errorRed:"#FF1B00",warningStrong:"#A45610",cautionAccent:"#D9821D",riskConcern:"#EFA700",sentiment:{up:"#07C168",neutral:"#FBC558",down:"#FF1B00"},badgeBgPositive:"rgba(7,193,104,0.12)",badgeBgCaution:"rgba(217,130,29,0.14)",badgeBgCritical:"rgba(185,28,28,0.12)",badgeBgNeutral:"rgba(110,102,96,0.12)",bubbleUser:"#10462E",bubbleAssistant:"#FFFFFF",skeleton:"#EFE9E3",scanButtonPink:"#FFC3BC",checkupCardLowRiskBg:"rgba(7,193,104,0.10)",checkupCardConcernBg:"rgba(239,167,0,0.10)"},hr=ye({brightness:"light",highContrast:false,meta:{displayName:"Everfur",logoUrl:null,placeholder:null,hidePoweredBy:false},color:on,space:yr,radius:mr,type:br(),motion:vr,slots:gr}),an={brand:"#6BD39A",brandSoft:"rgba(107,211,154,0.12)",brandSofter:"rgba(107,211,154,0.18)",brandBorder:"rgba(107,211,154,0.22)",borderHairlineGreen:"rgba(107,211,154,0.28)",brandBorderStrong:"rgba(107,211,154,0.55)",brandInk:"#6BD39A",linkInk:"#6BD39A",surface:"#1C1F23",surfaceCream:"#23272C",surfaceSubtle:"#23272C",background:"#15171A",borderRingCream:"#3A3F45",chatGround:"#1C1F23",composerFill:"#23272C",composerBorder:"rgba(107,211,154,0.75)",shadow:"#000000",text:{primary:"#F5F3F0",secondary:"#A9E4C4",muted:"#9A938C",placeholder:"rgba(255,255,255,0.55)",onBrand:"#0E1F17",metaCool:"#C9C9C9",timestamp:"rgba(245,243,240,0.7)"},primary:"#6BD39A",onPrimary:"#0E1F17",accent:"#6BD39A",onAccent:"#0E1F17",accentEdge:"#34D888",success:"#07C168",danger:"#F87171",errorRed:"#FF6B5B",warningStrong:"#F0A35E",cautionAccent:"#D9821D",riskConcern:"#EFA700",sentiment:{up:"#07C168",neutral:"#FBC558",down:"#FF1B00"},badgeBgPositive:"rgba(7,193,104,0.18)",badgeBgCaution:"rgba(217,130,29,0.20)",badgeBgCritical:"rgba(185,28,28,0.22)",badgeBgNeutral:"rgba(255,255,255,0.10)",bubbleUser:"#6BD39A",bubbleAssistant:"#1C1F23",skeleton:"#23272C",scanButtonPink:"#FFC3BC",checkupCardLowRiskBg:"rgba(7,193,104,0.16)",checkupCardConcernBg:"rgba(239,167,0,0.16)"};ye({brightness:"dark",highContrast:false,meta:{displayName:"Everfur",logoUrl:null,placeholder:null,hidePoweredBy:false},color:an,space:yr,radius:mr,type:br(),motion:vr,slots:gr});var Er=Symbol.for("@everfur/sdk#EverfurThemeContext@v1"),_r=globalThis,me=_r[Er]??(_r[Er]=react.createContext(hr));me.displayName??="EverfurThemeContext";function S(){return react.useContext(me)}o(S,"useEverfurTheme");function pn(e,r){let t=e.scaleCaps[r];return t>0?t:void 0}o(pn,"tierMultiplier");function E({tier:e="content",...r}){let{type:t}=S(),n=pn(t,e);return react.createElement(reactNative.Text,n===void 0?r:{maxFontSizeMultiplier:n,...r})}o(E,"Text");react.forwardRef(o(function(r,t){return react.createElement(reactNative.TextInput,{...r,...t===null?{}:{ref:t}})},"TextInput"));var fn=Object.freeze({regular:"regular",medium:"medium",bold:"bold",mono:"regular",monoMedium:"medium",monoBold:"bold"});function M(e,r){let t=e.type,n=t.families[r];return r==="mono"||t.namedFaces[r]?{fontFamily:n}:{fontFamily:n,fontWeight:t.weight[fn[r]]}}o(M,"face");var ve=class ve extends react.Component{constructor(){super(...arguments);this.state={failed:false,key:this.props.resetKey};}static getDerivedStateFromError(){return {failed:true}}static getDerivedStateFromProps(t,n){return Object.is(t.resetKey,n.key)?null:{failed:false,key:t.resetKey}}render(){return this.state.failed?this.props.fallback:this.props.children}};o(ve,"HostRenderBoundary");var be=ve;function mn({render:e}){return e()}o(mn,"RenderHost");function ge({render:e,fallback:r,resetKey:t}){return react.createElement(be,{fallback:r,resetKey:t,children:react.createElement(mn,{render:e})})}o(ge,"HostRender");var bn=Object.freeze({chat:Object.freeze(["chat.message.create"]),records:Object.freeze(["records.upload.create","records.record.read"]),recordsClinic:Object.freeze(["records.clinic.create"]),video:Object.freeze(["video.gait.execute"]),photo:Object.freeze(["photo.checkup.execute"]),animations:Object.freeze(["animations.state.read"]),televet:Object.freeze(["televet.visit.create"])});function gn(e){return bn[e]}o(gn,"requires");function Cr(e,r,t){let n=gn(e),i=new Map;for(let l of r)i.set(l.resource,l);if(t?.isPending===true&&n.some(l=>!i.has(l)))return he({name:e,enabled:false,isPending:true,limits:{}});let a=vn(n,i);return n.filter(l=>i.get(l)?.granted!==true).length===0?he({name:e,enabled:true,isPending:false,limits:a}):he({name:e,enabled:false,isPending:false,limits:a,disabled:hn(n,i)})}o(Cr,"decideCapability");function vn(e,r){let t={};for(let n of e){let i=r.get(n)?.limits;if(i)for(let[a,s]of Object.entries(i)){let l=t[a];t[a]=l===void 0?s:Math.min(l,s);}}return t}o(vn,"mergeLimits");function hn(e,r){for(let t of e){let n=r.get(t)?.disabled;if(n)return Object.freeze({reason:n.reason,level:n.level,recoverable:n.recoverable})}return Object.freeze({reason:oe("accessDenied"),level:"user_entitlement",recoverable:false})}o(hn,"disabledFor");function he(e){let r={name:e.name,enabled:e.enabled,isPending:e.isPending,limits:Object.freeze({...e.limits})};return e.disabled!==void 0&&(r.disabled=e.disabled),Object.freeze(r)}o(he,"freezeDecision");function Ee(e){let{runtime:r}=W(),t=r.entitlements,n=react.useSyncExternalStore(t.subscribe,t.getSnapshot,t.getSnapshot);return react.useMemo(()=>Cr(e,n.decisions,{isPending:n.isPending}),[e,n])}o(Ee,"useCapability");var Rn=48;function Sn(e){return e.isPending?"pending":e.enabled?"enabled":"disabled"}o(Sn,"selectView");function wr(e){let{capability:r,children:t,renderDisabled:n,renderPending:i,style:a}=e,s=Ee(r),l=react.useMemo(()=>Sn(s),[s]);switch(l){case "enabled":return t;case "pending":return i?jsxRuntime.jsx(ge,{render:i,fallback:jsxRuntime.jsx(kr,{capability:r,style:a})}):jsxRuntime.jsx(kr,{capability:r,style:a});case "disabled":return n?jsxRuntime.jsx(ge,{render:()=>n(s),fallback:jsxRuntime.jsx(xr,{decision:s,style:a}),resetKey:s}):jsxRuntime.jsx(xr,{decision:s,style:a});default:return H(l)}}o(wr,"CapabilityGate");function kr({capability:e,style:r}){let t=S(),n=react.useMemo(()=>Fr(t),[t]);return jsxRuntime.jsx(reactNative.View,{style:[n.offstate,r],accessible:true,accessibilityState:{busy:true},accessibilityLabel:`Loading ${e}`,children:jsxRuntime.jsx(reactNative.View,{style:n.skeletonBar})})}o(kr,"PendingSkeleton");function xr({decision:e,style:r}){let t=S(),n=react.useMemo(()=>Fr(t),[t]),i=e.disabled?.reason??"";return jsxRuntime.jsx(reactNative.View,{style:[n.offstate,r],accessible:true,accessibilityRole:"text",accessibilityLabel:i,children:i.length>0?jsxRuntime.jsx(E,{style:n.offstateText,children:i}):null})}o(xr,"OffState");function Fr(e){let{color:r,space:t,radius:n,type:i}=e;return reactNative.StyleSheet.create({offstate:{minHeight:Rn,justifyContent:"center",paddingHorizontal:t.lg,paddingVertical:t.md,backgroundColor:r.surfaceCream,borderRadius:n.md,borderWidth:reactNative.StyleSheet.hairlineWidth,borderColor:r.brandBorder},offstateText:{...M(e,"regular"),fontSize:i.size.body,lineHeight:Math.round(i.size.body*i.lineHeight.normal),color:r.text.muted},skeletonBar:{height:t.md,borderRadius:n.xs,backgroundColor:r.brandSofter}})}o(Fr,"makeStyles");var L=Object.freeze({title:"For educational use only",body:"Everfur is for educational purposes only. It helps you track visible changes through AI checkups. It does not diagnose, treat, prescribe, or replace veterinary care. For urgent symptoms or emergencies, contact a veterinarian immediately."});function Ir(e){let r=S(),t=react.useMemo(()=>kn(r),[r]);return jsxRuntime.jsxs(reactNative.View,{style:[t.container,e.style],accessible:true,accessibilityRole:"text",accessibilityLabel:`${L.title}. ${L.body}`,children:[jsxRuntime.jsx(E,{style:t.title,children:L.title}),jsxRuntime.jsx(E,{style:t.body,children:L.body})]})}o(Ir,"ClinicalDisclaimer");function kn(e){let{color:r,space:t,type:n}=e;return reactNative.StyleSheet.create({container:{paddingHorizontal:t.lg,paddingVertical:t.md,borderTopWidth:reactNative.StyleSheet.hairlineWidth,borderTopColor:r.borderHairlineGreen,backgroundColor:r.surfaceCream,gap:t.xs},title:{...M(e,"bold"),fontSize:n.size.caption,lineHeight:Math.round(n.size.caption*n.lineHeight.snug),color:r.text.secondary,letterSpacing:n.letterSpacing.eyebrow},body:{...M(e,"regular"),fontSize:n.size.caption,lineHeight:Math.round(n.size.caption*n.lineHeight.snug),color:r.text.muted}})}o(kn,"makeStyles");var In=300,Ce=120,Ar=44,zr=40,An=160;function zn(e){let{petRef:r,style:t}=e;return jsxRuntime.jsx(wr,{capability:"video",style:t,children:jsxRuntime.jsx(Dn,{petRef:r,style:t})})}o(zn,"EverfurVideo");function jn(e){return e.isBootstrapping||e.status==="uploading"||e.status==="analyzing"?"loading":e.status==="error"&&e.error?"error":e.status==="complete"&&e.result?"populated":e.isCaptureAvailable?"empty":"native"}o(jn,"selectView");function Dn({petRef:e,style:r}){let t=react.useMemo(()=>e!==void 0?{petRef:e}:void 0,[e]),n=ce(t),i=jn(n),{styles:a}=P();return jsxRuntime.jsxs(reactNative.View,{style:[a.container,r],children:[jsxRuntime.jsx(reactNative.View,{style:a.body,children:Nn(i,n)}),jsxRuntime.jsx(Ir,{})]})}o(Dn,"VideoBody");function Nn(e,r){switch(e){case "loading":return jsxRuntime.jsx(Mn,{progress:r.progress});case "native":return jsxRuntime.jsx(Ln,{});case "empty":return jsxRuntime.jsx(Un,{onCapture:r.capture});case "error":return jsxRuntime.jsx(Bn,{error:r.error,onRetry:r.capture});case "populated":return jsxRuntime.jsx(Vn,{result:r.result});default:return H(e)}}o(Nn,"renderView");function Mn({progress:e}){let{theme:r,styles:t}=P(),[n,i]=react.useState(false);react.useEffect(()=>{let s=setTimeout(()=>i(true),In);return ()=>clearTimeout(s)},[]);let a=Math.max(0,Math.min(1,e));return jsxRuntime.jsxs(reactNative.View,{style:t.centered,accessible:true,accessibilityState:{busy:true},accessibilityLabel:"Analyzing video",children:[n?jsxRuntime.jsx(reactNative.ActivityIndicator,{color:r.color.brand}):null,n&&a>0&&a<1?jsxRuntime.jsx(reactNative.View,{style:t.progressTrack,accessibilityRole:"progressbar",children:jsxRuntime.jsx(reactNative.View,{style:[t.progressFill,{width:`${Math.round(a*100)}%`}]})}):null]})}o(Mn,"LoadingState");function Ln(){let{styles:e}=P();return jsxRuntime.jsx(reactNative.View,{style:e.offstate,accessible:true,accessibilityRole:"text",accessibilityLabel:"Camera is unavailable",children:jsxRuntime.jsx(reactNative.View,{style:e.emptyGlyph})})}o(Ln,"NativeUnavailableState");function Un({onCapture:e}){let{styles:r}=P();return jsxRuntime.jsx(reactNative.View,{style:r.centered,accessible:true,accessibilityRole:"summary",accessibilityLabel:"Record a short clip to check gait",children:jsxRuntime.jsx(reactNative.Pressable,{onPress:e,accessibilityRole:"button",accessibilityLabel:"Record video",style:r.primaryButton,children:jsxRuntime.jsx(E,{style:r.primaryLabel,children:"Record video"})})})}o(Un,"EmptyState");function Bn({error:e,onRetry:r}){let{styles:t}=P();return jsxRuntime.jsxs(reactNative.View,{style:t.centered,accessible:true,accessibilityRole:"alert",children:[jsxRuntime.jsx(E,{style:t.errorText,children:e.displayMessage}),e.retryable?jsxRuntime.jsx(reactNative.Pressable,{onPress:r,accessibilityRole:"button",accessibilityLabel:"Try again",style:t.primaryButton,children:jsxRuntime.jsx(E,{style:t.primaryLabel,children:"Try again"})}):null]})}o(Bn,"ErrorState");function Vn({result:e}){let{styles:r}=P();return e.quality.usable?jsxRuntime.jsxs(reactNative.ScrollView,{style:r.list,contentContainerStyle:r.listContent,accessibilityLabel:"Gait analysis result",children:[e.detections.map((t,n)=>jsxRuntime.jsx(reactNative.View,{style:r.resultRow,children:jsxRuntime.jsx(E,{style:r.resultLabel,children:t.label})},`${t.label}-${n}`)),e.interpretation?jsxRuntime.jsxs(reactNative.View,{style:r.interpretationBlock,children:[jsxRuntime.jsx(E,{style:r.summaryText,children:e.interpretation.summary}),jsxRuntime.jsx(E,{style:r.disclaimerText,children:e.interpretation.disclaimer})]}):null]}):jsxRuntime.jsx(reactNative.View,{style:r.centered,accessible:true,accessibilityRole:"summary",accessibilityLabel:"Result uncertain",children:jsxRuntime.jsx(E,{style:r.uncertainText,children:"We could not get a clear read from this clip. Try recording again."})})}o(Vn,"ResultState");function P(){let e=S();return react.useMemo(()=>({theme:e,styles:qn(e)}),[e])}o(P,"useVideoStyles");function qn(e){let{color:r,space:t,radius:n,type:i}=e,a=i.weight.bold,s=i.weight.medium;return reactNative.StyleSheet.create({container:{flex:1,minHeight:Ce,backgroundColor:r.background},body:{flex:1},centered:{minHeight:Ce,flexGrow:1,alignItems:"center",justifyContent:"center",padding:t.xl},offstate:{minHeight:Ce,alignItems:"center",justifyContent:"center",padding:t.xl},emptyGlyph:{width:zr,height:zr,borderRadius:n.pill,backgroundColor:r.surfaceCream,borderWidth:reactNative.StyleSheet.hairlineWidth,borderColor:r.borderHairlineGreen},primaryButton:{minHeight:Ar,minWidth:Ar,alignItems:"center",justifyContent:"center",paddingHorizontal:t.xl,paddingVertical:t.md,borderRadius:n.pill,backgroundColor:r.primary,shadowColor:r.brandSofter,shadowOffset:{width:0,height:t.xs},shadowOpacity:1,shadowRadius:t.sm,elevation:t.xs},primaryLabel:{color:r.onPrimary,fontFamily:i.family,fontSize:i.size.body,fontWeight:a,letterSpacing:i.letterSpacing.tight},errorText:{color:r.danger,fontFamily:i.family,fontSize:i.size.subtitle,lineHeight:i.size.subtitle*i.lineHeight.normal,fontWeight:s,textAlign:"center",marginBottom:t.md},uncertainText:{color:r.text.muted,fontFamily:i.family,fontSize:i.size.subtitle,lineHeight:i.size.subtitle*i.lineHeight.normal,textAlign:"center"},progressTrack:{marginTop:t.lg,height:t.sm,width:An,borderRadius:n.pill,backgroundColor:r.skeleton,overflow:"hidden"},progressFill:{height:t.sm,borderRadius:n.pill,backgroundColor:r.brand},list:{flex:1},listContent:{padding:t.lg,gap:t.md},resultRow:{paddingVertical:t.sm,paddingHorizontal:t.md,borderRadius:n.sm,backgroundColor:r.surfaceCream,borderWidth:reactNative.StyleSheet.hairlineWidth,borderColor:r.brandBorder},resultLabel:{color:r.text.primary,fontFamily:i.family,fontSize:i.size.body,lineHeight:i.size.body*i.lineHeight.normal,fontWeight:s},interpretationBlock:{marginTop:t.sm,gap:t.sm},summaryText:{color:r.text.primary,fontFamily:i.family,fontSize:i.size.subtitle,lineHeight:i.size.subtitle*i.lineHeight.normal},disclaimerText:{color:r.text.muted,fontFamily:i.family,fontSize:i.size.caption,lineHeight:i.size.caption*i.lineHeight.relaxed}})}o(qn,"makeStyles");exports.EverfurVideo=zn;exports.useEverfurVideo=ce;