@everfur/sdk 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +648 -1
- package/README.md +58 -23
- package/consent/package.json +8 -0
- package/dist/Chat-DPUD6dnc.d.cts +128 -0
- package/dist/Chat-DwAC2vhB.d.ts +128 -0
- package/dist/DepthViews-DNEM96Se.d.ts +16 -0
- package/dist/DepthViews-DP0R-DhB.d.cts +19 -0
- package/dist/DepthViews-D_3hl4xk.d.ts +19 -0
- package/dist/DepthViews-DbFhR4Du.d.cts +16 -0
- package/dist/ErrorPolicyPort-CNf4uQZP.d.cts +22 -0
- package/dist/ErrorPolicyPort-CwbfmxzJ.d.ts +22 -0
- package/dist/{EverfurResult-D92-uL82.d.cts → EverfurResult-DN9pL2Ab.d.cts} +11 -10
- package/dist/{EverfurResult-D92-uL82.d.ts → EverfurResult-DN9pL2Ab.d.ts} +11 -10
- package/dist/{PhotoController-BItt5M7u.d.cts → PhotoController-3l7MT9jA.d.cts} +1 -1
- package/dist/{PhotoController-D8zMTdcW.d.ts → PhotoController-vzY5bfY_.d.ts} +1 -1
- package/dist/animations/index.cjs +1 -1997
- package/dist/animations/index.d.cts +35 -35
- package/dist/animations/index.d.ts +35 -35
- package/dist/animations/index.js +1 -1972
- package/dist/attachments-5NeobOCd.d.ts +256 -0
- package/dist/attachments-Bw3iMQgu.d.ts +146 -0
- package/dist/attachments-CQgFrEQ7.d.cts +146 -0
- package/dist/attachments-D8T5re7e.d.cts +256 -0
- package/dist/casesRepository-ClLv6qMl.d.ts +188 -0
- package/dist/casesRepository-DHwtGRYY.d.cts +188 -0
- package/dist/chat/index.cjs +1 -1170
- package/dist/chat/index.d.cts +31 -53
- package/dist/chat/index.d.ts +31 -53
- package/dist/chat/index.js +1 -1167
- package/dist/client/index.cjs +9 -2315
- package/dist/client/index.d.cts +77 -15
- package/dist/client/index.d.ts +77 -15
- package/dist/client/index.js +9 -2215
- package/dist/{config-CiJ0PVBB.d.ts → config-CzTZGlfF.d.cts} +63 -25
- package/dist/{config-BSjBdxrZ.d.cts → config-DW60uRb9.d.ts} +63 -25
- package/dist/consent/index.cjs +1 -0
- package/dist/consent/index.d.cts +33 -0
- package/dist/consent/index.d.ts +33 -0
- package/dist/consent/index.js +1 -0
- package/dist/context-DXhqHlPU.d.ts +159 -0
- package/dist/context-XvTzq1tE.d.cts +159 -0
- package/dist/copy-Mjoo91aS.d.ts +198 -0
- package/dist/copy-bKDNigLb.d.cts +198 -0
- package/dist/core/index.cjs +13 -3175
- package/dist/core/index.d.cts +58 -12
- package/dist/core/index.d.ts +58 -12
- package/dist/core/index.js +13 -3163
- package/dist/depth-CErXCU8J.d.cts +502 -0
- package/dist/depth-DjDcJ6kH.d.ts +502 -0
- package/dist/entitlementRepository-DKdXFgTo.d.cts +698 -0
- package/dist/entitlementRepository-DSbzuyAW.d.ts +698 -0
- package/dist/{identity-DK9zORrG.d.ts → identity-BMx2SUOM.d.ts} +19 -6
- package/dist/{identity-Brl-lDd6.d.cts → identity-DI4eVx9S.d.cts} +19 -6
- package/dist/{ids-CJ1S6adf.d.cts → ids-B2GAAifq.d.cts} +1 -1
- package/dist/{ids-CJ1S6adf.d.ts → ids-B2GAAifq.d.ts} +1 -1
- package/dist/index.cjs +12 -4488
- package/dist/index.d.cts +29 -121
- package/dist/index.d.ts +29 -121
- package/dist/index.js +12 -4464
- package/dist/models-B-Uh2LTf.d.ts +214 -0
- package/dist/models-D2EUxPZY.d.cts +214 -0
- package/dist/notifications/index.cjs +2 -0
- package/dist/notifications/index.d.cts +17 -0
- package/dist/notifications/index.d.ts +17 -0
- package/dist/notifications/index.js +2 -0
- package/dist/pets-CtQdRpWo.d.ts +92 -0
- package/dist/pets-aB8q0JyX.d.cts +92 -0
- package/dist/photo/index.cjs +1 -2189
- package/dist/photo/index.d.cts +6 -6
- package/dist/photo/index.d.ts +6 -6
- package/dist/photo/index.js +1 -2186
- package/dist/ports-BN6RHF9W.d.cts +48 -0
- package/dist/ports-BN6RHF9W.d.ts +48 -0
- package/dist/ports-DEBzEhbp.d.cts +386 -0
- package/dist/ports-DMolTRzU.d.ts +386 -0
- package/dist/profile-CSs1wlXT.d.cts +93 -0
- package/dist/profile-CSs1wlXT.d.ts +93 -0
- package/dist/records/depth/index.cjs +2 -0
- package/dist/records/depth/index.d.cts +13 -0
- package/dist/records/depth/index.d.ts +13 -0
- package/dist/records/depth/index.js +2 -0
- package/dist/records/index.cjs +3 -2839
- package/dist/records/index.d.cts +137 -203
- package/dist/records/index.d.ts +137 -203
- package/dist/records/index.js +3 -2833
- package/dist/requestFunnel-CjW7uDuA.d.ts +87 -0
- package/dist/requestFunnel-L-BQfi0z.d.cts +87 -0
- package/dist/{resolve-Dq_4_agU.d.ts → resolve-D4Ywz5OS.d.cts} +10 -4
- package/dist/{resolve-Dq_4_agU.d.cts → resolve-D4Ywz5OS.d.ts} +10 -4
- package/dist/{runtime-BgQnA594.d.cts → runtime-B8s-_hv8.d.cts} +134 -59
- package/dist/{runtime-CBA-LvdM.d.ts → runtime-BR9ysG0J.d.ts} +134 -59
- package/dist/server/events/index.cjs +2 -0
- package/dist/server/events/index.d.cts +550 -0
- package/dist/server/events/index.d.ts +550 -0
- package/dist/server/events/index.js +2 -0
- package/dist/server/index.cjs +2 -533
- package/dist/server/index.d.cts +73 -18
- package/dist/server/index.d.ts +73 -18
- package/dist/server/index.js +2 -530
- package/dist/species-BXAIMh7I.d.cts +9 -0
- package/dist/species-BXAIMh7I.d.ts +9 -0
- package/dist/televet/index.cjs +1 -0
- package/dist/televet/index.d.cts +60 -0
- package/dist/televet/index.d.ts +60 -0
- package/dist/televet/index.js +1 -0
- package/dist/testing/index.cjs +5 -823
- package/dist/testing/index.d.cts +17 -3
- package/dist/testing/index.d.ts +17 -3
- package/dist/testing/index.js +5 -820
- package/dist/testing/rn/index.cjs +1 -449
- package/dist/testing/rn/index.d.cts +17 -29
- package/dist/testing/rn/index.d.ts +17 -29
- package/dist/testing/rn/index.js +1 -444
- package/dist/testing/web/index.cjs +1 -0
- package/dist/testing/web/index.d.cts +131 -0
- package/dist/testing/web/index.d.ts +131 -0
- package/dist/testing/web/index.js +1 -0
- package/dist/timelineRows-CaohZzXS.d.cts +19 -0
- package/dist/timelineRows-DZed6dAM.d.ts +19 -0
- package/dist/typeStyle-0TydiZ1w.d.cts +19 -0
- package/dist/typeStyle-CWMsoe6b.d.ts +19 -0
- package/dist/uploadTransport-D0M0T4hN.d.ts +38 -0
- package/dist/uploadTransport-DKJHs3Yj.d.cts +38 -0
- package/dist/useRecordsDepth-BHBzJ76R.d.cts +237 -0
- package/dist/useRecordsDepth-BU-OhzH6.d.ts +237 -0
- package/dist/useVetVisit-Bc84hWUU.d.cts +187 -0
- package/dist/useVetVisit-CjmMa896.d.ts +187 -0
- package/dist/video/index.cjs +1 -1941
- package/dist/video/index.d.cts +4 -4
- package/dist/video/index.d.ts +4 -4
- package/dist/video/index.js +1 -1938
- package/dist/view-C3qPIXGX.d.cts +310 -0
- package/dist/view-DtSPYpKa.d.ts +310 -0
- package/dist/visitIntent-D7_yVp1I.d.cts +8 -0
- package/dist/visitIntent-D7_yVp1I.d.ts +8 -0
- package/dist/web/consent/index.cjs +1 -0
- package/dist/web/consent/index.d.cts +34 -0
- package/dist/web/consent/index.d.ts +34 -0
- package/dist/web/consent/index.js +1 -0
- package/dist/web/index.cjs +14 -0
- package/dist/web/index.d.cts +217 -0
- package/dist/web/index.d.ts +217 -0
- package/dist/web/index.js +14 -0
- package/dist/web/notifications/index.cjs +2 -0
- package/dist/web/notifications/index.d.cts +213 -0
- package/dist/web/notifications/index.d.ts +213 -0
- package/dist/web/notifications/index.js +2 -0
- package/dist/web/records/depth/index.cjs +2 -0
- package/dist/web/records/depth/index.d.cts +12 -0
- package/dist/web/records/depth/index.d.ts +12 -0
- package/dist/web/records/depth/index.js +2 -0
- package/dist/web/records/index.cjs +4 -0
- package/dist/web/records/index.d.cts +194 -0
- package/dist/web/records/index.d.ts +194 -0
- package/dist/web/records/index.js +4 -0
- package/dist/web/televet/index.cjs +1 -0
- package/dist/web/televet/index.d.cts +50 -0
- package/dist/web/televet/index.d.ts +50 -0
- package/dist/web/televet/index.js +1 -0
- package/notifications/package.json +8 -0
- package/package.json +197 -9
- package/records/depth/package.json +8 -0
- package/server/events/device-blocked.cjs +15 -0
- package/server/events/package.json +9 -0
- package/televet/package.json +8 -0
- package/testing/web/native-blocked.cjs +12 -0
- package/testing/web/package.json +8 -0
- package/web/consent/native-blocked.cjs +12 -0
- package/web/consent/package.json +8 -0
- package/web/native-blocked.cjs +12 -0
- package/web/notifications/native-blocked.cjs +12 -0
- package/web/notifications/package.json +8 -0
- package/web/package.json +8 -0
- package/web/records/depth/native-blocked.cjs +12 -0
- package/web/records/depth/package.json +8 -0
- package/web/records/native-blocked.cjs +12 -0
- package/web/records/package.json +8 -0
- package/web/televet/native-blocked.cjs +12 -0
- package/web/televet/package.json +8 -0
- package/dist/ChatController-CKdBvPj2.d.ts +0 -146
- package/dist/ChatController-CpUMvvZf.d.cts +0 -146
- package/dist/FilePort-BabWrv7I.d.cts +0 -22
- package/dist/FilePort-BabWrv7I.d.ts +0 -22
- package/dist/petsRepository-BEGb97M9.d.cts +0 -326
- package/dist/petsRepository-Bu18r2kK.d.ts +0 -326
- package/dist/requestFunnel-DuUH-kAe.d.cts +0 -28
- package/dist/requestFunnel-dio5OmR9.d.ts +0 -28
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import { b as PartnerPetProfile } from '../../pets-CtQdRpWo.js';
|
|
2
|
+
import { c as EverfurResult } from '../../EverfurResult-DN9pL2Ab.js';
|
|
3
|
+
import { j as FetchLike } from '../../config-DW60uRb9.js';
|
|
4
|
+
export { b as PetActivityLevel, c as PetAllergy, d as PetAllergySeverity, e as PetBirthDatePrecision, f as PetCondition, P as PetHealthProfileInput, g as PetIndoorOutdoor, h as PetMedication, i as PetSex } from '../../profile-CSs1wlXT.js';
|
|
5
|
+
|
|
6
|
+
/** Every outbound event type in the catalog. Order matches EFBackend `OUTBOUND_EVENT_TYPES`. */
|
|
7
|
+
declare const EVERFUR_WEBHOOK_EVENT_TYPES: readonly ["member.linked", "member.unlinked", "visit.booked", "visit.cancelled", "visit.completed", "visit.no_show", "record_request.updated", "record.ready", "webhook_endpoint.test", "chat.follow_up_due", "chat.urgency.flagged", "pet.vaccination.due", "visit.rescheduled", "visit.reminder", "visit.followup_sent", "chat.reminder_due", "record.activation", "visit.message_received", "visit.ended"];
|
|
8
|
+
/** One outbound event type name. */
|
|
9
|
+
type EverfurWebhookEventType = (typeof EVERFUR_WEBHOOK_EVENT_TYPES)[number];
|
|
10
|
+
/**
|
|
11
|
+
* The types that reach your WEBHOOK ONLY and can never reach a member's device, whatever the member's
|
|
12
|
+
* notification settings say (EFBackend `partner_notifications/channels.py` NO_COPY_TYPES, which is also why
|
|
13
|
+
* `partner_target.py` builds no push target for them):
|
|
14
|
+
*
|
|
15
|
+
* - `member.linked` / `member.unlinked` are your bookkeeping, not news for the member;
|
|
16
|
+
* - `chat.urgency.flagged` is an in-app affordance, not a push;
|
|
17
|
+
* - `webhook_endpoint.test` is the console's own test button.
|
|
18
|
+
*
|
|
19
|
+
* Every OTHER catalog type does build a push target, so it can announce itself on a device and
|
|
20
|
+
* `parseEverfurNotification` can read it (three of them, `visit.completed`, `visit.rescheduled` and
|
|
21
|
+
* `visit.ended`, have a target but no consumer copy, so in practice no push is sent for them either).
|
|
22
|
+
* Read this list when you decide which events your own backend must act on rather than leave to the device.
|
|
23
|
+
*/
|
|
24
|
+
declare const EVERFUR_WEBHOOK_ONLY_EVENT_TYPES: readonly ["member.linked", "member.unlinked", "chat.urgency.flagged", "webhook_endpoint.test"];
|
|
25
|
+
/** One type that never reaches a device. */
|
|
26
|
+
type EverfurWebhookOnlyEventType = (typeof EVERFUR_WEBHOOK_ONLY_EVENT_TYPES)[number];
|
|
27
|
+
/** Fields any payload may carry beyond the ones typed here (forward compatible). */
|
|
28
|
+
interface OpenEventPayload {
|
|
29
|
+
readonly [field: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
/** `member.linked` / `member.unlinked`: a partner member's link to an Everfur account changed. */
|
|
32
|
+
interface MemberLinkPayload extends OpenEventPayload {
|
|
33
|
+
readonly object: 'member';
|
|
34
|
+
/** Your own opaque id for the member, as you passed it when minting the session. */
|
|
35
|
+
readonly user_ref: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* What the partner may know a visit to be. A visit in progress or awaiting its note is still `booked`; every
|
|
39
|
+
* terminal consult status maps to one of the other three (`VISIT_STATUS_BY_CONSULT_STATUS`), and a visit
|
|
40
|
+
* nobody attended and nobody cancelled is a `no_show` that blames no one.
|
|
41
|
+
*/
|
|
42
|
+
type EverfurVisitStatus = 'booked' | 'cancelled' | 'completed' | 'no_show';
|
|
43
|
+
/**
|
|
44
|
+
* The fields every `visit.*` event carries (`VISIT_EVENT_DATA_KEYS`). Sent only for a visit that started from
|
|
45
|
+
* your integration, and only when the member opted in to share visit status with you.
|
|
46
|
+
*/
|
|
47
|
+
interface VisitPayload extends OpenEventPayload {
|
|
48
|
+
readonly object: 'visit';
|
|
49
|
+
/**
|
|
50
|
+
* The visit's opaque partner reference (the platform's `partner_visit_ref`, minted when a visit starts from
|
|
51
|
+
* your integration). Never an internal consult id, and the same on every event for that visit; a follow-up
|
|
52
|
+
* visit gets its own.
|
|
53
|
+
*/
|
|
54
|
+
readonly visit_ref: string;
|
|
55
|
+
readonly user_ref: string;
|
|
56
|
+
/** Your pet id, or `null` when you never named a pet for this link (or the owner has not confirmed one). */
|
|
57
|
+
readonly pet_ref: string | null;
|
|
58
|
+
/** The partner-visible status of the visit at the moment of the event. */
|
|
59
|
+
readonly status: EverfurVisitStatus;
|
|
60
|
+
/** ISO 8601. The visit's scheduled time; on `visit.rescheduled` it is already the NEW time. */
|
|
61
|
+
readonly scheduled_at: string;
|
|
62
|
+
/** ISO 8601. When the change happened (the status transition, the reschedule, the reminder or the send). */
|
|
63
|
+
readonly occurred_at: string;
|
|
64
|
+
}
|
|
65
|
+
/** `visit.rescheduled`: the owner moved the visit. `status` is `booked`. */
|
|
66
|
+
interface VisitRescheduledPayload extends VisitPayload {
|
|
67
|
+
readonly status: 'booked';
|
|
68
|
+
/** ISO 8601. The time the visit was scheduled for before this move. */
|
|
69
|
+
readonly previous_scheduled_at: string;
|
|
70
|
+
}
|
|
71
|
+
/** The two consumer reminder rows, by lead time (`VISIT_REMINDER_STAGE_BY_OUTBOX_EVENT`). */
|
|
72
|
+
type EverfurVisitReminderStage = '24h' | '30m';
|
|
73
|
+
/** `visit.reminder`: the member's reminder for a visit still to happen was released. `status` is `booked`. */
|
|
74
|
+
interface VisitReminderPayload extends VisitPayload {
|
|
75
|
+
readonly status: 'booked';
|
|
76
|
+
readonly stage: EverfurVisitReminderStage;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* `visit.followup_sent`: the post-visit check-in message was delivered to the member. `status` is what the
|
|
80
|
+
* partner may know the visit to be at that moment: `completed`, or `booked` while the note is still pending.
|
|
81
|
+
*/
|
|
82
|
+
interface VisitFollowupSentPayload extends VisitPayload {
|
|
83
|
+
readonly status: 'booked' | 'completed';
|
|
84
|
+
/** ISO 8601. When the check-in was sent. Never the message. */
|
|
85
|
+
readonly sent_at: string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* `visit.message_received` (owner decision 4): the vet wrote in the visit thread. One per message. Ids and one
|
|
89
|
+
* timestamp: never the message, a preview or the vet's name.
|
|
90
|
+
*/
|
|
91
|
+
interface VisitMessageReceivedPayload extends VisitPayload {
|
|
92
|
+
/** ISO 8601. When the vet's message was written. */
|
|
93
|
+
readonly message_at: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* `visit.ended` (owner decision 4): the call ended, before the note is signed, so your list can stop reading
|
|
97
|
+
* "booked" for a visit that has happened. One per visit. The status is still `booked` until the note is signed and
|
|
98
|
+
* `visit.completed` arrives.
|
|
99
|
+
*/
|
|
100
|
+
interface VisitEndedPayload extends VisitPayload {
|
|
101
|
+
/** ISO 8601. When the call ended. */
|
|
102
|
+
readonly ended_at: string;
|
|
103
|
+
}
|
|
104
|
+
/** The seven simple statuses of the one records status model (`records_simple_status.SimpleStatus`). */
|
|
105
|
+
type RecordRequestSimpleStatus = 'requested' | 'awaiting_clinic' | 'received' | 'parsing' | 'published' | 'failed' | 'revoked';
|
|
106
|
+
/**
|
|
107
|
+
* `record_request.updated`: a records request reached a new simple state, or a reviewer amended the record after
|
|
108
|
+
* it was published (one more `published` event per amendment). Field set pinned by EFBackend
|
|
109
|
+
* `tests/records/test_records_partner_events.py`.
|
|
110
|
+
*/
|
|
111
|
+
interface RecordRequestUpdatedPayload extends OpenEventPayload {
|
|
112
|
+
readonly object: 'record_request';
|
|
113
|
+
readonly request_id: string;
|
|
114
|
+
readonly user_ref: string;
|
|
115
|
+
readonly pet_ref: string;
|
|
116
|
+
/** The request's state in the seven-value simple status model. Never the raw status. */
|
|
117
|
+
readonly simple_status: RecordRequestSimpleStatus;
|
|
118
|
+
/**
|
|
119
|
+
* The partner-safe detail code, or `null` when the status says it all. The closed allowlist today is
|
|
120
|
+
* `awaiting_owner_upload`, `received_without_document`, `in_review`, `clinic_declined`, `no_response`,
|
|
121
|
+
* `hard_bounce` and `clinic_replied_without_records`; it can grow, so handle a value you do not know.
|
|
122
|
+
*/
|
|
123
|
+
readonly simple_status_detail: string | null;
|
|
124
|
+
/** The consumer app's action-first sentence for an action-needed state, or `null`. Render verbatim. */
|
|
125
|
+
readonly action_needed_label: string | null;
|
|
126
|
+
/** The request's own recovery policy: what the member could do next, as the consumer app shows it. */
|
|
127
|
+
readonly can_retry_send: boolean;
|
|
128
|
+
readonly can_update_clinic_email: boolean;
|
|
129
|
+
readonly can_revoke: boolean;
|
|
130
|
+
readonly can_convert_to_upload: boolean;
|
|
131
|
+
/** ISO 8601. When the PUBLISHED record last changed (the latest amendment, else the publish); `null` before. */
|
|
132
|
+
readonly record_updated_at: string | null;
|
|
133
|
+
}
|
|
134
|
+
/** `record.ready`: a requested record is readable through the records API. Carries no record content. */
|
|
135
|
+
interface RecordReadyPayload extends OpenEventPayload {
|
|
136
|
+
readonly object: 'record';
|
|
137
|
+
readonly request_id: string;
|
|
138
|
+
readonly user_ref: string;
|
|
139
|
+
readonly pet_ref: string;
|
|
140
|
+
}
|
|
141
|
+
/** The catalog vaccines a due-date reminder names (never a product label). */
|
|
142
|
+
type EverfurVaccine = 'rabies' | 'dhpp';
|
|
143
|
+
/** The consumer reminder stages a due date reaches: 14 days out, 3 days out, then overdue. */
|
|
144
|
+
type EverfurVaccinationDueStage = 'due_in_14d' | 'due_in_3d' | 'overdue';
|
|
145
|
+
/**
|
|
146
|
+
* `pet.vaccination.due`: a widget pet's rabies or DHPP due date reached a reminder stage. One event per (pet,
|
|
147
|
+
* vaccine, due date, stage), from the same snapshot the consumer app reminds from. Ids and closed codes only.
|
|
148
|
+
*/
|
|
149
|
+
interface PetVaccinationDuePayload extends OpenEventPayload {
|
|
150
|
+
readonly object: 'pet_vaccination';
|
|
151
|
+
readonly user_ref: string;
|
|
152
|
+
readonly pet_ref: string;
|
|
153
|
+
readonly vaccine: EverfurVaccine;
|
|
154
|
+
/** `YYYY-MM-DD`. */
|
|
155
|
+
readonly due_at: string;
|
|
156
|
+
readonly stage: EverfurVaccinationDueStage;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* `chat.follow_up_due`: a follow-up check-in for a watched case is due. The partner's webhook replaces the
|
|
160
|
+
* consumer push; the in-thread check-in itself arrives through the conversation list. Ids and one timestamp,
|
|
161
|
+
* never the check-in sentence.
|
|
162
|
+
*/
|
|
163
|
+
interface ChatFollowUpDuePayload extends OpenEventPayload {
|
|
164
|
+
readonly object: 'chat_follow_up';
|
|
165
|
+
readonly user_ref: string;
|
|
166
|
+
readonly pet_ref: string;
|
|
167
|
+
/** The opaque case id (`WatchCase.caseId`). */
|
|
168
|
+
readonly case_ref: string;
|
|
169
|
+
/** The thread the check-in lands in when one exists, else `null`. */
|
|
170
|
+
readonly conversation_id: string | null;
|
|
171
|
+
/** ISO 8601. */
|
|
172
|
+
readonly due_at: string;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* `chat.urgency.flagged`: an assistant reply carried an emergency urgency verdict. One per flagged message.
|
|
176
|
+
* Ids plus the closed level; no reason, no text. The backend writes no `pet_ref` on this event: the thread
|
|
177
|
+
* is named by `conversation_id`.
|
|
178
|
+
*/
|
|
179
|
+
interface ChatUrgencyFlaggedPayload extends OpenEventPayload {
|
|
180
|
+
readonly object: 'chat_urgency';
|
|
181
|
+
readonly user_ref: string;
|
|
182
|
+
readonly conversation_id: string;
|
|
183
|
+
/** The assistant message that carries the verdict. */
|
|
184
|
+
readonly message_id: string;
|
|
185
|
+
/** `emergency` today (`FLAGGED_URGENCY_LEVELS`); the level set can grow. */
|
|
186
|
+
readonly urgency_level: string;
|
|
187
|
+
}
|
|
188
|
+
/** Which of the two memory nudges is waiting in the pet's chat (`REMINDER_KIND_BY_TRIGGER`). A closed code. */
|
|
189
|
+
type EverfurChatReminderKind = 'medication_due' | 'open_concern';
|
|
190
|
+
/**
|
|
191
|
+
* `chat.reminder_due` (CRN-18): the consumer app's medication reminder, or its one gentle check-in on an unresolved
|
|
192
|
+
* concern, is waiting in the pet's chat. One per (pet, kind, due day), under the same claim, pause and opt-out fences
|
|
193
|
+
* the consumer push has. Ids and a closed kind: never the medicine, the symptom or the nudge sentence.
|
|
194
|
+
*/
|
|
195
|
+
interface ChatReminderDuePayload extends OpenEventPayload {
|
|
196
|
+
readonly object: 'chat_reminder';
|
|
197
|
+
readonly user_ref: string;
|
|
198
|
+
readonly pet_ref: string;
|
|
199
|
+
/** The thread the reminder lands in when one exists, else `null`. */
|
|
200
|
+
readonly conversation_id: string | null;
|
|
201
|
+
readonly kind: EverfurChatReminderKind;
|
|
202
|
+
/** ISO 8601. When the nudge became due. */
|
|
203
|
+
readonly due_at: string;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* The closed classes of thing a parsed record can hold (`copy/records_activation.py` RECORD_CLASSES), in the
|
|
207
|
+
* order the platform counts them. A `record.activation` event names exactly one of these.
|
|
208
|
+
*/
|
|
209
|
+
type EverfurRecordClass = 'weight_history' | 'vaccinations' | 'medications' | 'bloodwork' | 'diagnoses';
|
|
210
|
+
/**
|
|
211
|
+
* `record.activation` (CRN-19): a requested record was read and something was found in it, so a member is told
|
|
212
|
+
* WHAT was found rather than only that records are ready.
|
|
213
|
+
*
|
|
214
|
+
* ONE EVENT PER NON-EMPTY CLASS, the way the consumer app sends one paced push per class: `record_class` names
|
|
215
|
+
* the class this event is about and `count` is how many rows of it the parse found. `counts` carries all five
|
|
216
|
+
* for the same request, so a single event is enough to render a summary. Counts and a closed class name only:
|
|
217
|
+
* never a weight, a drug, a result or a diagnosis.
|
|
218
|
+
*
|
|
219
|
+
* SEVERAL OF THESE FOR ONE REQUEST IS NORMAL, not a duplicate: one per non-empty class, deduped by the
|
|
220
|
+
* platform on `request:class`. An empty class produces no event at all, so never wait for a zero-count ping,
|
|
221
|
+
* and never dedupe your own handling on `request_id` alone or you will swallow every class but the first.
|
|
222
|
+
*/
|
|
223
|
+
interface RecordActivationPayload extends OpenEventPayload {
|
|
224
|
+
readonly object: 'record_activation';
|
|
225
|
+
readonly request_id: string;
|
|
226
|
+
readonly user_ref: string;
|
|
227
|
+
readonly pet_ref: string;
|
|
228
|
+
/** Which class this event is about. One event per non-empty class of one request. */
|
|
229
|
+
readonly record_class: EverfurRecordClass;
|
|
230
|
+
/** How many rows of `record_class` the parse found. Always at least 1: an empty class sends nothing. */
|
|
231
|
+
readonly count: number;
|
|
232
|
+
/** All five counts for the same request, so one event can render the whole summary. */
|
|
233
|
+
readonly counts: Readonly<Record<EverfurRecordClass, number>>;
|
|
234
|
+
}
|
|
235
|
+
/** `webhook_endpoint.test`: sent when you press Send test on an endpoint. Names the endpoint it was sent to. */
|
|
236
|
+
interface WebhookEndpointTestPayload extends OpenEventPayload {
|
|
237
|
+
readonly object: 'webhook_endpoint';
|
|
238
|
+
readonly endpoint_id: string;
|
|
239
|
+
}
|
|
240
|
+
/** Payload type for each outbound event type. */
|
|
241
|
+
interface EverfurWebhookPayloadMap {
|
|
242
|
+
readonly 'member.linked': MemberLinkPayload;
|
|
243
|
+
readonly 'member.unlinked': MemberLinkPayload;
|
|
244
|
+
readonly 'visit.booked': VisitPayload;
|
|
245
|
+
readonly 'visit.cancelled': VisitPayload;
|
|
246
|
+
readonly 'visit.completed': VisitPayload;
|
|
247
|
+
readonly 'visit.no_show': VisitPayload;
|
|
248
|
+
readonly 'record_request.updated': RecordRequestUpdatedPayload;
|
|
249
|
+
readonly 'record.ready': RecordReadyPayload;
|
|
250
|
+
readonly 'webhook_endpoint.test': WebhookEndpointTestPayload;
|
|
251
|
+
readonly 'chat.follow_up_due': ChatFollowUpDuePayload;
|
|
252
|
+
readonly 'chat.urgency.flagged': ChatUrgencyFlaggedPayload;
|
|
253
|
+
readonly 'pet.vaccination.due': PetVaccinationDuePayload;
|
|
254
|
+
readonly 'visit.rescheduled': VisitRescheduledPayload;
|
|
255
|
+
readonly 'visit.reminder': VisitReminderPayload;
|
|
256
|
+
readonly 'visit.followup_sent': VisitFollowupSentPayload;
|
|
257
|
+
readonly 'chat.reminder_due': ChatReminderDuePayload;
|
|
258
|
+
readonly 'record.activation': RecordActivationPayload;
|
|
259
|
+
readonly 'visit.message_received': VisitMessageReceivedPayload;
|
|
260
|
+
readonly 'visit.ended': VisitEndedPayload;
|
|
261
|
+
}
|
|
262
|
+
/** The signed envelope of one outbound event of type `T`. */
|
|
263
|
+
interface EverfurWebhookEventOf<T extends EverfurWebhookEventType> {
|
|
264
|
+
/** `evt_...`. Unique per event; a manual resend reuses it on purpose, so dedupe on it. */
|
|
265
|
+
readonly id: string;
|
|
266
|
+
readonly object: 'event';
|
|
267
|
+
readonly type: T;
|
|
268
|
+
/** The payload version, for example `2026-09-15`. */
|
|
269
|
+
readonly api_version: string;
|
|
270
|
+
/** Unix seconds when the event was created (not when it was delivered). */
|
|
271
|
+
readonly created: number;
|
|
272
|
+
/** `false` for a sandbox (test) tenant. */
|
|
273
|
+
readonly livemode: boolean;
|
|
274
|
+
readonly data: {
|
|
275
|
+
readonly object: EverfurWebhookPayloadMap[T];
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* A verified outbound event whose type is in the catalog. After `isEverfurWebhookEvent(event)`, narrow on
|
|
280
|
+
* `type`: `if (event.type === 'record.ready') event.data.object.request_id`.
|
|
281
|
+
*/
|
|
282
|
+
type EverfurWebhookEvent = {
|
|
283
|
+
[T in EverfurWebhookEventType]: EverfurWebhookEventOf<T>;
|
|
284
|
+
}[EverfurWebhookEventType];
|
|
285
|
+
/**
|
|
286
|
+
* A verified outbound event whose type this SDK version does not know (a type added after it shipped).
|
|
287
|
+
* `constructEvent` returns it rather than throwing, because the signature is valid and the right answer to an
|
|
288
|
+
* event you do not handle is still a 2xx. Separate it with `isEverfurWebhookEvent` before narrowing on `type`.
|
|
289
|
+
*/
|
|
290
|
+
interface UnrecognizedEverfurWebhookEvent {
|
|
291
|
+
readonly id: string;
|
|
292
|
+
readonly object: 'event';
|
|
293
|
+
readonly type: string;
|
|
294
|
+
readonly api_version: string;
|
|
295
|
+
readonly created: number;
|
|
296
|
+
readonly livemode: boolean;
|
|
297
|
+
readonly data: {
|
|
298
|
+
readonly object: OpenEventPayload;
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/** True when `type` is in this SDK version's outbound catalog. */
|
|
302
|
+
declare function isEverfurWebhookEventType(type: unknown): type is EverfurWebhookEventType;
|
|
303
|
+
/**
|
|
304
|
+
* Separates a catalog event from one this SDK version does not know, so a `switch (event.type)` narrows the
|
|
305
|
+
* payload. Answer 2xx to the other branch: Everfur retries anything else.
|
|
306
|
+
*/
|
|
307
|
+
declare function isEverfurWebhookEvent(event: EverfurWebhookEvent | UnrecognizedEverfurWebhookEvent): event is EverfurWebhookEvent;
|
|
308
|
+
/** Every inbound event type in the v1 registry. Order matches EFBackend `INBOUND_EVENT_TYPES`. */
|
|
309
|
+
declare const PARTNER_INBOUND_EVENT_TYPES: readonly ["pet.profile.updated", "pet.incident.reported", "pet.behavior_note.added", "pet.service_score.recorded", "pet.deleted", "member.status_changed", "member.deleted"];
|
|
310
|
+
/** One inbound event type name. A `pet.*` type is about a pet (`petRef` required); a `member.*` type is not. */
|
|
311
|
+
type PartnerInboundEventType = (typeof PARTNER_INBOUND_EVENT_TYPES)[number];
|
|
312
|
+
/**
|
|
313
|
+
* `pet.profile.updated` schema_version 1: profile fields you are the system of record for. AUTHORITATIVE: it updates
|
|
314
|
+
* the pet profile itself, in the event's own transaction (PF-11), so an applied receipt never names a write that did
|
|
315
|
+
* not happen. Every field is optional and an absent field (or `null`) is unchanged: the event API has no clear, use
|
|
316
|
+
* `updatePartnerPet` for that. Strings are trimmed by Everfur before their length is checked. `date_of_birth` is
|
|
317
|
+
* recorded as the birth date at day precision and `neutered` as the spay status; for a pet that is not a dog or a
|
|
318
|
+
* cat those three are accepted and not applied (v1 is best effort there), while the identity fields still land.
|
|
319
|
+
*/
|
|
320
|
+
interface PetProfileUpdatedData {
|
|
321
|
+
/** 1 to 64 characters. */
|
|
322
|
+
readonly name?: string | null;
|
|
323
|
+
/** 1 to 64 characters. */
|
|
324
|
+
readonly species?: string | null;
|
|
325
|
+
/** 1 to 128 characters. */
|
|
326
|
+
readonly breed?: string | null;
|
|
327
|
+
readonly sex?: 'male' | 'female' | 'unknown' | null;
|
|
328
|
+
/** `YYYY-MM-DD`. */
|
|
329
|
+
readonly date_of_birth?: string | null;
|
|
330
|
+
/** Greater than 0 and less than 500. */
|
|
331
|
+
readonly weight_kg?: number | null;
|
|
332
|
+
readonly neutered?: boolean | null;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* `pet.incident.reported`. `incident_id` is the natural id: a later event with the same id replaces this one when
|
|
336
|
+
* its `occurredAt` is later.
|
|
337
|
+
*/
|
|
338
|
+
interface PetIncidentReportedData {
|
|
339
|
+
/** Your id for the incident, 1 to 128 characters. */
|
|
340
|
+
readonly incident_id: string;
|
|
341
|
+
readonly category: 'injury' | 'illness' | 'behavior' | 'escape' | 'other';
|
|
342
|
+
readonly severity: 'low' | 'medium' | 'high';
|
|
343
|
+
readonly location_type: 'daycare' | 'boarding' | 'grooming' | 'walk' | 'training' | 'home' | 'other';
|
|
344
|
+
/** 1 to 2000 characters. */
|
|
345
|
+
readonly description: string;
|
|
346
|
+
/** Defaults to `false`. */
|
|
347
|
+
readonly resolved?: boolean;
|
|
348
|
+
}
|
|
349
|
+
/** `pet.behavior_note.added`. `note_id` is the natural id. */
|
|
350
|
+
interface PetBehaviorNoteAddedData {
|
|
351
|
+
/** Your id for the note, 1 to 128 characters. */
|
|
352
|
+
readonly note_id: string;
|
|
353
|
+
readonly context: 'daycare' | 'boarding' | 'grooming' | 'walk' | 'training' | 'other';
|
|
354
|
+
/** 1 to 2000 characters. */
|
|
355
|
+
readonly note: string;
|
|
356
|
+
readonly sentiment?: 'positive' | 'neutral' | 'concern' | null;
|
|
357
|
+
}
|
|
358
|
+
/** `pet.service_score.recorded`. `score_id` is the natural id. */
|
|
359
|
+
interface PetServiceScoreRecordedData {
|
|
360
|
+
/** Your id for the score, 1 to 128 characters. */
|
|
361
|
+
readonly score_id: string;
|
|
362
|
+
readonly service: 'daycare' | 'boarding' | 'grooming' | 'walk' | 'training';
|
|
363
|
+
/** An integer from 1 to 5. */
|
|
364
|
+
readonly score: number;
|
|
365
|
+
/** At most 2000 characters. */
|
|
366
|
+
readonly comment?: string | null;
|
|
367
|
+
}
|
|
368
|
+
/** `pet.deleted`: erases what you sent about this pet. */
|
|
369
|
+
interface PetDeletedData {
|
|
370
|
+
readonly reason?: 'owner_request' | 'deceased' | 'rehomed' | 'other' | null;
|
|
371
|
+
}
|
|
372
|
+
/** `member.status_changed`: the member's status in your service. */
|
|
373
|
+
interface MemberStatusChangedData {
|
|
374
|
+
readonly status: 'active' | 'paused' | 'cancelled' | 'lapsed';
|
|
375
|
+
/** At most 64 characters. */
|
|
376
|
+
readonly plan?: string | null;
|
|
377
|
+
/** `YYYY-MM-DD`. */
|
|
378
|
+
readonly effective_on?: string | null;
|
|
379
|
+
}
|
|
380
|
+
/** `member.deleted`: erases what you sent about this member. */
|
|
381
|
+
interface MemberDeletedData {
|
|
382
|
+
readonly reason?: 'owner_request' | 'account_closed' | 'other' | null;
|
|
383
|
+
}
|
|
384
|
+
/** The `data` schema of each inbound event type (schema version 1). */
|
|
385
|
+
interface PartnerInboundEventDataMap {
|
|
386
|
+
readonly 'pet.profile.updated': PetProfileUpdatedData;
|
|
387
|
+
readonly 'pet.incident.reported': PetIncidentReportedData;
|
|
388
|
+
readonly 'pet.behavior_note.added': PetBehaviorNoteAddedData;
|
|
389
|
+
readonly 'pet.service_score.recorded': PetServiceScoreRecordedData;
|
|
390
|
+
readonly 'pet.deleted': PetDeletedData;
|
|
391
|
+
readonly 'member.status_changed': MemberStatusChangedData;
|
|
392
|
+
readonly 'member.deleted': MemberDeletedData;
|
|
393
|
+
}
|
|
394
|
+
/** One event of type `T` your backend sends to Everfur. */
|
|
395
|
+
interface PartnerInboundEventOf<T extends PartnerInboundEventType> {
|
|
396
|
+
readonly type: T;
|
|
397
|
+
/**
|
|
398
|
+
* The schema version of `data`. Every type is at version 1; omit it or pass 1. `pet.profile.updated` also has
|
|
399
|
+
* version 2, the whole partner pet body (`PetProfileUpdatedV2Event`).
|
|
400
|
+
*/
|
|
401
|
+
readonly schemaVersion?: 1;
|
|
402
|
+
/** Your opaque member id (the same one you mint sessions with). 1 to 128 printable characters, no `@`. */
|
|
403
|
+
readonly userRef: string;
|
|
404
|
+
/**
|
|
405
|
+
* Your opaque pet id, under the same rule as `userRef`. Required on a `pet.*` event and refused on a
|
|
406
|
+
* `member.*` event (`sendPartnerEvent` throws before sending either mistake).
|
|
407
|
+
*/
|
|
408
|
+
readonly petRef?: string;
|
|
409
|
+
/**
|
|
410
|
+
* When it happened: a `Date`, or an ISO 8601 date-time string with an explicit offset (`Z` or `+05:30`). Everfur
|
|
411
|
+
* refuses a time older than 30 days or more than 5 minutes ahead of its clock.
|
|
412
|
+
*/
|
|
413
|
+
readonly occurredAt: Date | string;
|
|
414
|
+
/** The type's fields. Closed: a field not in the type's schema is refused. */
|
|
415
|
+
readonly data: PartnerInboundEventDataMap[T];
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* `pet.profile.updated` schema_version 2 (PF-11): the whole partner pet body, health profile included, exactly what
|
|
419
|
+
* `upsertPartnerPet` takes. Everfur applies it through the same write path the pet routes use, onto the same tables,
|
|
420
|
+
* so what Everfur chat and an Everfur vet see is the same whichever door you use.
|
|
421
|
+
*
|
|
422
|
+
* - Absent is unchanged and there is NO CLEAR over the event API (nulls are dropped): use `updatePartnerPet` to clear.
|
|
423
|
+
* A health LIST you send is still the complete set you hold (set-replace, `[]` clears it).
|
|
424
|
+
* - A health field needs `pets.health.update` as well as `partner_events.inbound.create`, and a dog or a cat: any
|
|
425
|
+
* other species is the 422 `pet_health_species_unsupported` with nothing applied.
|
|
426
|
+
* - Version 2 rides the server pet API's own switch: while Everfur has it off, version 2 is refused exactly as a
|
|
427
|
+
* version that does not exist (400 `event_payload_invalid`).
|
|
428
|
+
* - A member you have not brought to Everfur is accepted and updates nothing; it never creates an account.
|
|
429
|
+
*/
|
|
430
|
+
interface PetProfileUpdatedV2Event {
|
|
431
|
+
readonly type: 'pet.profile.updated';
|
|
432
|
+
readonly schemaVersion: 2;
|
|
433
|
+
/** Your opaque member id (the same one you mint sessions with). 1 to 128 printable characters, no `@`. */
|
|
434
|
+
readonly userRef: string;
|
|
435
|
+
/** Your opaque pet id, under the same rule as `userRef`. */
|
|
436
|
+
readonly petRef: string;
|
|
437
|
+
/** When it happened: a `Date`, or an ISO 8601 date-time string with an explicit offset. */
|
|
438
|
+
readonly occurredAt: Date | string;
|
|
439
|
+
/** The partner pet body, as `upsertPartnerPet` takes it. Only the fields it names are sent. */
|
|
440
|
+
readonly data: PartnerPetProfile;
|
|
441
|
+
}
|
|
442
|
+
/** One event your backend sends to Everfur. Narrow on `type` (and `schemaVersion`) to get that type's `data`. */
|
|
443
|
+
type PartnerInboundEvent = {
|
|
444
|
+
[T in PartnerInboundEventType]: PartnerInboundEventOf<T>;
|
|
445
|
+
}[PartnerInboundEventType] | PetProfileUpdatedV2Event;
|
|
446
|
+
|
|
447
|
+
/** The request header carrying the signature. Header lookups are case-insensitive in Node. */
|
|
448
|
+
declare const EVERFUR_SIGNATURE_HEADER = "Everfur-Signature";
|
|
449
|
+
/** Default replay window, in seconds, matching the platform. */
|
|
450
|
+
declare const DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
|
|
451
|
+
/** Why a delivery was refused. Programmer errors (no secret, a parsed body) throw `EverfurConfigError`. */
|
|
452
|
+
type WebhookVerificationFailure =
|
|
453
|
+
/** The header is missing, too long, or not `t=...,v1=...`. */
|
|
454
|
+
'malformed_header'
|
|
455
|
+
/** The signed timestamp is outside the tolerance (a replay, or a clock that is far off). */
|
|
456
|
+
| 'timestamp_outside_tolerance'
|
|
457
|
+
/** No `v1` matches any of the secrets. Wrong secret, or the body was changed after signing. */
|
|
458
|
+
| 'no_matching_signature'
|
|
459
|
+
/** The signature is valid but the body is not an Everfur event envelope. */
|
|
460
|
+
| 'malformed_payload';
|
|
461
|
+
/** Thrown by `constructEvent` when a delivery must be refused. Answer it with a 400. */
|
|
462
|
+
declare class EverfurWebhookVerificationError extends Error {
|
|
463
|
+
readonly code: WebhookVerificationFailure;
|
|
464
|
+
constructor(code: WebhookVerificationFailure);
|
|
465
|
+
}
|
|
466
|
+
/** Options for `constructEvent`. */
|
|
467
|
+
interface ConstructEventOptions {
|
|
468
|
+
/** Replay window in seconds, applied both ways. Defaults to 300. */
|
|
469
|
+
readonly toleranceSeconds?: number;
|
|
470
|
+
/** The current time in unix seconds. For tests; defaults to the system clock. */
|
|
471
|
+
readonly nowSeconds?: number;
|
|
472
|
+
}
|
|
473
|
+
/** A raw request body: the exact bytes received, or those bytes decoded as UTF-8. Never parsed JSON. */
|
|
474
|
+
type WebhookPayload = string | Uint8Array;
|
|
475
|
+
/**
|
|
476
|
+
* Verify an Everfur webhook delivery and return the event it carries.
|
|
477
|
+
*
|
|
478
|
+
* Pass the RAW body (a `Buffer` from `express.raw()`, or `await request.text()`), the `Everfur-Signature`
|
|
479
|
+
* header, and your endpoint's signing secret. During a rotation pass both secrets. Throws
|
|
480
|
+
* `EverfurWebhookVerificationError` when the delivery must be refused (answer 400), and `EverfurConfigError`
|
|
481
|
+
* for a call that can never succeed (no secret, a parsed object for a body, a bad tolerance).
|
|
482
|
+
*
|
|
483
|
+
* The result may be an event type this SDK version does not know yet; `isEverfurWebhookEvent` separates the
|
|
484
|
+
* two before you switch on `type`. Deduplicate on `event.id`: a resend carries the same id on purpose.
|
|
485
|
+
*/
|
|
486
|
+
declare function constructEvent(payload: WebhookPayload, signatureHeader: string | null | undefined, secrets: string | readonly string[], options?: ConstructEventOptions): EverfurWebhookEvent | UnrecognizedEverfurWebhookEvent;
|
|
487
|
+
/** Options for `generateTestSignatureHeader`. */
|
|
488
|
+
interface TestSignatureHeaderParams {
|
|
489
|
+
readonly payload: WebhookPayload;
|
|
490
|
+
/** Current secret first, then any grace secrets, the order Everfur sends them in. */
|
|
491
|
+
readonly secrets: string | readonly string[];
|
|
492
|
+
/** Unix seconds. Defaults to now. */
|
|
493
|
+
readonly timestamp?: number;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Build an `Everfur-Signature` header exactly as Everfur would, for testing your webhook handler with a
|
|
497
|
+
* test secret. Never needed in production.
|
|
498
|
+
*/
|
|
499
|
+
declare function generateTestSignatureHeader(params: TestSignatureHeaderParams): string;
|
|
500
|
+
|
|
501
|
+
/** How your backend reaches Everfur. Build it once and reuse it for every event. */
|
|
502
|
+
interface PartnerEventsClient {
|
|
503
|
+
/** The Everfur API base URL, for example `https://api.everfur.com/api/v1`. https only (loopback excepted). */
|
|
504
|
+
readonly apiBaseUrl: string;
|
|
505
|
+
/**
|
|
506
|
+
* `sk_partner_...`, or an `rk_partner_...` restricted key carrying `partner_events.inbound.create`. Never shipped
|
|
507
|
+
* to a device.
|
|
508
|
+
*/
|
|
509
|
+
readonly partnerSecretKey: string;
|
|
510
|
+
readonly contractVersion?: string;
|
|
511
|
+
readonly fetchImpl?: FetchLike;
|
|
512
|
+
}
|
|
513
|
+
/** What Everfur recorded for one inbound event: the 201 receipt. A replay of the same request answers the same one. */
|
|
514
|
+
interface PartnerEventReceipt {
|
|
515
|
+
/** `ievt_...`. */
|
|
516
|
+
readonly id: string;
|
|
517
|
+
readonly type: PartnerInboundEventType;
|
|
518
|
+
readonly schemaVersion: number;
|
|
519
|
+
readonly userRef: string;
|
|
520
|
+
/** The pet the event is about, or `null` for a member event. */
|
|
521
|
+
readonly petRef: string | null;
|
|
522
|
+
/** ISO 8601, in UTC, as Everfur stored it. */
|
|
523
|
+
readonly occurredAt: string;
|
|
524
|
+
/** ISO 8601, in UTC. */
|
|
525
|
+
readonly receivedAt: string;
|
|
526
|
+
/**
|
|
527
|
+
* `applied`, or `superseded` when an event for the same fact with a later `occurredAt` already landed. A later
|
|
528
|
+
* read of the event can also show `erased`, after a `pet.deleted` or `member.deleted`.
|
|
529
|
+
*/
|
|
530
|
+
readonly status: string;
|
|
531
|
+
/** `false` for a sandbox (test) tenant. */
|
|
532
|
+
readonly livemode: boolean;
|
|
533
|
+
/** The key the request was sent with (the one you passed, or the one generated). */
|
|
534
|
+
readonly idempotencyKey: string;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Send one event about a member or a pet to Everfur.
|
|
538
|
+
*
|
|
539
|
+
* `idempotencyKey` makes a retry safe: send the same key with the same event and Everfur records it once (the
|
|
540
|
+
* SDK serialises one event to the same bytes every time). Omit it and a random key is generated, but it is only
|
|
541
|
+
* returned on a successful receipt: after a timeout or a network failure it is lost, and a resend gets a new key
|
|
542
|
+
* and can record the event twice. Pass your own key (an id from your system) whenever you might retry.
|
|
543
|
+
*
|
|
544
|
+
* Settles to an `EverfurResult` for HTTP and network failures. Throws `EverfurConfigError` for a request that can
|
|
545
|
+
* never be accepted: a malformed key or event, a pet event without `petRef` (or a member event with one), an
|
|
546
|
+
* `occurredAt` without an offset, or a body over 32 KB.
|
|
547
|
+
*/
|
|
548
|
+
declare function sendPartnerEvent(client: PartnerEventsClient, event: PartnerInboundEvent, idempotencyKey?: string): Promise<EverfurResult<PartnerEventReceipt>>;
|
|
549
|
+
|
|
550
|
+
export { type ChatFollowUpDuePayload, type ChatReminderDuePayload, type ChatUrgencyFlaggedPayload, type ConstructEventOptions, DEFAULT_WEBHOOK_TOLERANCE_SECONDS, EVERFUR_SIGNATURE_HEADER, EVERFUR_WEBHOOK_EVENT_TYPES, EVERFUR_WEBHOOK_ONLY_EVENT_TYPES, type EverfurChatReminderKind, type EverfurRecordClass, type EverfurVaccinationDueStage, type EverfurVaccine, type EverfurVisitReminderStage, type EverfurVisitStatus, type EverfurWebhookEvent, type EverfurWebhookEventOf, type EverfurWebhookEventType, type EverfurWebhookOnlyEventType, type EverfurWebhookPayloadMap, EverfurWebhookVerificationError, type MemberDeletedData, type MemberLinkPayload, type MemberStatusChangedData, type OpenEventPayload, PARTNER_INBOUND_EVENT_TYPES, type PartnerEventReceipt, type PartnerEventsClient, type PartnerInboundEvent, type PartnerInboundEventDataMap, type PartnerInboundEventOf, type PartnerInboundEventType, PartnerPetProfile, type PetBehaviorNoteAddedData, type PetDeletedData, type PetIncidentReportedData, type PetProfileUpdatedData, type PetProfileUpdatedV2Event, type PetServiceScoreRecordedData, type PetVaccinationDuePayload, type RecordActivationPayload, type RecordReadyPayload, type RecordRequestSimpleStatus, type RecordRequestUpdatedPayload, type TestSignatureHeaderParams, type UnrecognizedEverfurWebhookEvent, type VisitEndedPayload, type VisitFollowupSentPayload, type VisitMessageReceivedPayload, type VisitPayload, type VisitReminderPayload, type VisitRescheduledPayload, type WebhookEndpointTestPayload, type WebhookPayload, type WebhookVerificationFailure, constructEvent, generateTestSignatureHeader, isEverfurWebhookEvent, isEverfurWebhookEventType, sendPartnerEvent };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {timingSafeEqual,createHmac,randomUUID}from'crypto';var we=Object.defineProperty;var n=(e,t)=>we(e,"name",{value:t,configurable:true});function k(e,t){return Object.freeze(t===void 0?{ok:true,value:e}:{ok:true,value:e,warning:t})}n(k,"ok");function _(e){return Object.freeze({ok:false,error:e})}n(_,"err");var E=class E extends Error{constructor(t,r){super(r),this.name="EverfurConfigError",this.kind=t,Object.setPrototypeOf(this,E.prototype);}};n(E,"EverfurConfigError");var s=E;var ke="Everfur-Signature",N=300,j="v1",Oe=4096,Te=64,xe=/^[0-9]+$/,R=class R extends Error{constructor(t){super(`Everfur webhook verification failed: ${t}`),this.name="EverfurWebhookVerificationError",this.code=t,Object.setPrototypeOf(this,R.prototype);}};n(R,"EverfurWebhookVerificationError");var p=R;function Ie(e,t,r,o={}){let i=L(e),a=M(r),l=o.toleranceSeconds??N;if(!Number.isInteger(l)||l<0)throw new s("invalid-argument","toleranceSeconds must be a non-negative integer");let d=De(t),u=o.nowSeconds??Math.floor(Date.now()/1e3);if(Math.abs(u-d.timestamp)>l)throw new p("timestamp_outside_tolerance");let c=false;for(let f of a){let y=Buffer.from(U(f,d.timestamp,i),"ascii");for(let m of d.signatures){if(m.length!==Te)continue;let g=Buffer.from(m,"utf8");g.length===y.length&&timingSafeEqual(y,g)&&(c=true);}}if(!c)throw new p("no_matching_signature");return Fe(i)}n(Ie,"constructEvent");function Ae(e){let t=L(e.payload),r=M(e.secrets),o=e.timestamp??Math.floor(Date.now()/1e3);if(!Number.isSafeInteger(o)||o<0)throw new s("invalid-argument","timestamp must be a non-negative integer (unix seconds)");return [`t=${o}`,...r.map(i=>`${j}=${U(i,o,t)}`)].join(",")}n(Ae,"generateTestSignatureHeader");function U(e,t,r){return createHmac("sha256",Buffer.from(e,"utf8")).update(`${t}.`,"ascii").update(r).digest("hex")}n(U,"computeSignature");function L(e){if(typeof e=="string")return Buffer.from(e,"utf8");if(e instanceof Uint8Array)return e;throw new s("invalid-argument","constructEvent needs the raw request body (a string or a Buffer), not parsed JSON: re-serialised JSON does not reproduce the signed bytes.")}n(L,"rawBodyBytes");function M(e){let t=typeof e=="string"?[e]:Array.isArray(e)?e:[];if(t.length===0||t.some(r=>typeof r!="string"||r.length===0))throw new s("invalid-argument","at least one non-empty webhook signing secret is required");return t}n(M,"requireSecrets");function De(e){if(typeof e!="string"||e.length===0||e.length>Oe)throw new p("malformed_header");let t=null,r=[];for(let o of e.split(",")){let i=o.trim(),a=i.indexOf("=");if(a<0)throw new p("malformed_header");let l=i.slice(0,a),d=i.slice(a+1);if(l==="t"){if(t!==null||!xe.test(d))throw new p("malformed_header");let u=Number(d);if(!Number.isSafeInteger(u))throw new p("malformed_header");t=u;}else l===j&&r.push(d);}if(t===null||r.length===0)throw new p("malformed_header");return {timestamp:t,signatures:r}}n(De,"parseSignatureHeader");function Fe(e){let t;try{t=JSON.parse(Buffer.from(e.buffer,e.byteOffset,e.byteLength).toString("utf8"));}catch{throw new p("malformed_payload")}if(!O(t))throw new p("malformed_payload");let{id:r,object:o,type:i,api_version:a,created:l,livemode:d,data:u}=t;if(typeof r!="string"||r.length===0||o!=="event"||typeof i!="string"||i.length===0||typeof a!="string"||typeof l!="number"||typeof d!="boolean"||!O(u)||!O(u.object))throw new p("malformed_payload");return t}n(Fe,"parseEnvelope");function O(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}n(O,"isRecord");var V="0.3.0",B="react-native",q="2026-09-19",z="EVERFUR_API_BASE_URL";function h(e,t){return Object.freeze({ok:false,failure:Object.freeze({reason:e,message:t})})}n(h,"configFailure");function Ne(e){let t=e?.trim(),r=t&&t.length>0?t:qe(z);return r?Ve(r):h("missing-api-base-url",`apiBaseUrl is required: pass config.apiBaseUrl or set ${z}. The SDK never ships a hardcoded host.`)}n(Ne,"tryResolveApiBaseUrl");function W(e){let t=Ne(e);if(!t.ok)throw new s("invalid-argument",t.failure.message);return t.value}n(W,"resolveApiBaseUrl");function H(e){return {"X-Everfur-SDK-Version":e.sdkVersion,"X-Everfur-SDK-Platform":e.platform,"X-Everfur-Contract":e.contractVersion}}n(H,"buildBaseHeaders");var je=/^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^/?#]*)/;function Ue(e){return e.slice(e.lastIndexOf("@")+1).replace(/:\d+$/,"").toLowerCase()}n(Ue,"hostOf");function Le(e){return e.length===0||e.includes("@")?false:e.startsWith("[")&&e.endsWith("]")?true:!e.includes(":")}n(Le,"isPrintableHost");function Me(e,t){let r=e.indexOf("://");return r<0||e.slice(r+3).includes("@")?false:Le(t)}n(Me,"canEchoOrigin");function ze(e,t){return `${e}://${t}`}n(ze,"safeOrigin");function Ve(e){let t=e.trim(),r=je.exec(t);if(r===null||r[2]===void 0||r[2].length===0)return h("malformed-api-base-url","apiBaseUrl is not a valid absolute URL");let o=(r[1]??"").toLowerCase(),i=Ue(r[2]);if(i.length===0)return h("malformed-api-base-url","apiBaseUrl is not a valid absolute URL");let a=Me(t,i)?`: ${ze(o,i)}`:"";if(o!=="https"){if(!(o==="http"&&Be(i)))return o==="http"?h("insecure-api-base-url",`apiBaseUrl must use https (cleartext http is only allowed for localhost)${a}`):h("unsupported-api-base-url-scheme",`apiBaseUrl must be http(s)${a}`)}let l=t.endsWith("/")?t.slice(0,-1):t;return Object.freeze({ok:true,value:l})}n(Ve,"normalizeBaseUrl");function Be(e){let t=e.toLowerCase();return t==="localhost"||t==="127.0.0.1"||t==="[::1]"||t==="::1"}n(Be,"isLoopbackHost");function qe(e){let r=globalThis.process?.env?.[e];return typeof r=="string"&&r.trim().length>0?r.trim():void 0}n(qe,"readEnv");var T=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(T.map(e=>e.wireCode));var We=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"}),K=Object.freeze(T.map(e=>Object.freeze({wireCode:e.wireCode,envelope:e.envelope,retryable:e.retryable,normalized:We[e.wireCode]}))),He=new Map(K.map(e=>[e.envelope+e.wireCode.toUpperCase(),e])),$e=Object.freeze(["http_envelope",...[...new Set(K.map(e=>e.envelope))].filter(e=>e!=="http_envelope").sort()]);function P(e,t){if(typeof e!="string")return;let r=e.trim().toUpperCase();if(r.length!==0)for(let o of [t??"http_envelope",...$e]){let i=He.get(o+r);if(i)return i}}n(P,"contractErrorFor");function Ke(e){return e.envelope==="sse_frame"?`/errors#sse-${e.wireCode}`:e.envelope==="oauth_error"?`/errors#oauth-${e.wireCode}`:`/errors#${e.wireCode}`}n(Ke,"contractDocUrl");function Y(e){return null}n(Y,"publishedDocUrl");var Ye=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 G(e){return Ye[e]}n(G,"categoryFor");function X(e){let t=P(e);if(t)return t.normalized;let r=(e??"").trim().toUpperCase(),o=r.replace(/[^A-Z0-9]/g,"");if(o==="AUTHREJECTED")return "authRejected";if(r.startsWith("AUTH"))return "authExpired";switch(o){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"}}n(X,"fromWire");function J(e){return e===401?"authExpired":e===403?"accessDenied":e===404?"conversationNotFound":e===409?"idempotencyConflict":e===421?"unknown":e===429?"rateLimited":e===400||e===422?"unknown":e>=500?"internalError":"unknown"}n(J,"fromHttpStatus");var Ge=new Set(["streamTimeout","rateLimited","internalError","upstreamLlmTimeout","streamIncomplete"]);function Q(e){return Ge.has(e)}n(Q,"isAutoRetryable");var Xe=new Set(["serviceUnavailable","conflict","idempotencyInFlight"]);function Z(e){return Q(e)||Xe.has(e)}n(Z,"isRetryableCode");var Je=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 Qe(e){return Je[e]}n(Qe,"userFacingError");function ee(e,t,r){Object.defineProperty(e,t,{value:r,enumerable:false,writable:false,configurable:false});}n(ee,"defineHidden");var Ze=/^[a-z][a-z0-9_]{0,63}$/;function x(e,t){let r=P(t?.wireCode,t?.wireEnvelope),o={code:e,category:G(e),retryable:t?.retryable??Z(e),displayMessage:Qe(e)};if(t?.httpStatus!==void 0&&(o.httpStatus=t.httpStatus),t?.requestId&&(o.requestId=t.requestId),typeof t?.reasonCode=="string"&&Ze.test(t.reasonCode)&&(o.reasonCode=t.reasonCode),r);return t?.wireMessage&&ee(o,"wireMessage",t.wireMessage),t?.raw!==void 0&&t.raw!==null&&ee(o,"raw",t.raw),Object.freeze(o)}n(x,"makeEverfurError");var et="x-request-id";function I(e,t,r){let o=tt(t),i=o?X(o):J(e),a=rt(t);return x(i,{httpStatus:e,...nt(t)?{retryable:true}:{},...r?{requestId:r}:{},...o?{wireCode:o}:{},...te(t)?{reasonCode:te(t)}:{},...a?{wireMessage:a}:{},...t!=null?{raw:t}:{}})}n(I,"errorFromResponse");function re(e){return x("internalError",{retryable:true,wireMessage:ot(e)})}n(re,"networkError");function ne(e){let t=e.headers.get(et);return typeof t=="string"&&t.length>0?t:null}n(ne,"requestIdOf");function oe(){let e=globalThis.fetch;if(typeof e!="function")throw new s("missing-peer-dependency","global fetch is unavailable; provide config.transport or an injected fetch implementation");return e}n(oe,"getGlobalFetch");function te(e){let t=e,r=t?.error?.reason_code??t?.reason_code;return typeof r=="string"&&r.length>0?r:null}n(te,"extractReasonCode");function tt(e){let t=e,r=t?.error?.code??t?.code;return typeof r=="string"&&r.length>0?r:null}n(tt,"extractWireCode");function rt(e){let t=e,r=t?.error?.message??t?.message;return typeof r=="string"&&r.length>0?r:null}n(rt,"extractWireMessage");function nt(e){let t=e;return t?.error?.retryable===true||t?.retryable===true}n(nt,"extractRetryable");function ot(e){return e instanceof Error?e.message:typeof e=="string"?e:"network request failed"}n(ot,"describeCause");var ie=Object.freeze(["member.linked","member.unlinked","visit.booked","visit.cancelled","visit.completed","visit.no_show","record_request.updated","record.ready","webhook_endpoint.test","chat.follow_up_due","chat.urgency.flagged","pet.vaccination.due","visit.rescheduled","visit.reminder","visit.followup_sent","chat.reminder_due","record.activation","visit.message_received","visit.ended"]),it=Object.freeze(["member.linked","member.unlinked","chat.urgency.flagged","webhook_endpoint.test"]);function ae(e){return typeof e=="string"&&ie.includes(e)}n(ae,"isEverfurWebhookEventType");function at(e){return ae(e.type)}n(at,"isEverfurWebhookEvent");var w=Object.freeze(["pet.profile.updated","pet.incident.reported","pet.behavior_note.added","pet.service_score.recorded","pet.deleted","member.status_changed","member.deleted"]);var se=128;function S(e){let t=typeof e=="string"?[...e].length:0;if(typeof e!="string"||t<1||t>se)throw new s("invalid-argument",`user_ref must be 1..${se} characters`);if(e.trim().length===0)throw new s("invalid-argument","user_ref must not be blank: the server strips surrounding whitespace before validating, so a whitespace-only value arrives empty and is rejected as required.");if(e!==e.trim())throw new s("invalid-argument","user_ref must not have leading or trailing whitespace: the server strips it, so padded and unpadded forms of the same id resolve to ONE user. Pass the exact id you want to key on.");if(e.includes("@"))throw new s("invalid-argument","user_ref must not contain '@'");if(!st(e))throw new s("invalid-argument","user_ref must be printable (no control characters)");return e}n(S,"assertValidUserRef");function st(e){return !lt.test(e)}n(st,"isPrintable");var lt=/[\p{Cc}\p{Cf}\p{Cs}\p{Co}\p{Cn}\p{Zl}\p{Zp}]|(?!\u0020)[\p{Zs}]/u;function le(e){for(let t=0;t<e.length;t+=1){let r=e.charCodeAt(t);if(r>=55296&&r<=56319){let o=e.charCodeAt(t+1);if(!(o>=56320&&o<=57343))return true;t+=1;}else if(r>=56320&&r<=57343)return true}return false}n(le,"hasLoneSurrogate");function de(e){return [...e].length}n(de,"serverLength");var ue=Object.freeze({dog:"dog",canine:"dog",k9:"dog",puppy:"dog",cat:"cat",feline:"cat",kitten:"cat"});function ce(e){if(typeof e!="string")return null;let t=e.trim().toLowerCase();return Object.prototype.hasOwnProperty.call(ue,t)?ue[t]:null}n(ce,"canonicalPetSpecies");var pe=50,dt=128,A=120,fe=120,ut=2e3,ye=120,ct=80,pt=new Set(["male","female","unknown"]),ft=new Set(["day","month","year"]),yt=new Set(["low","medium","high","athletic"]),mt=new Set(["indoor","outdoor","both"]),vt=new Set(["mild","moderate","severe"]),gt=/^(\d{4})-(\d{2})-(\d{2})$/,ve=[["sex","sex"],["spayedNeutered","spayed_neutered"],["birthDate","birth_date"],["birthDatePrecision","birth_date_precision"],["activityLevel","activity_level"],["indoorOutdoor","indoor_outdoor"],["foodBrand","food_brand"],["foodType","food_type"],["microchipId","microchip_id"]],ge=[["allergies","allergies"],["medications","medications"],["supplements","supplements"],["conditions","conditions"]];Object.freeze([...ve,...ge].map(([e])=>e));function _t(e){let t=gt.exec(e);if(t===null)return false;let[r,o,i]=[Number(t[1]),Number(t[2]),Number(t[3])],a=new Date(Date.UTC(r,o-1,i));return a.getUTCFullYear()===r&&a.getUTCMonth()===o-1&&a.getUTCDate()===i}n(_t,"isRealDate");var b=n(e=>(t,r)=>typeof r=="string"&&e.has(r)?null:`${t} must be one of ${[...e].join(", ")}`,"oneOf"),ht=n((e,t)=>typeof t=="boolean"?null:`${e} must be true or false`,"boolean"),C=n((e,t)=>typeof t=="string"&&_t(t)?null:`${e} must be a real date written YYYY-MM-DD`,"date"),v=n((e,t={})=>(r,o)=>{if(typeof o!="string")return `${r} must be a string`;if(le(o))return `${r} contains an unpaired surrogate and cannot be encoded`;let i=t.trimmed?o.trim():o;if(t.required&&i.length===0)return `${r} is required and must not be blank`;let a=de(i);return a<=e?null:`${r} is ${a} characters; the server accepts at most ${e}`},"text"),bt={sex:b(pt),spayedNeutered:ht,birthDate:C,birthDatePrecision:b(ft),activityLevel:b(yt),indoorOutdoor:b(mt),foodBrand:v(ye),foodType:v(ye),microchipId:v(ct)},D=["ref","ref",false,v(dt,{required:true,trimmed:true})],F=["notes","notes",false,v(ut,{trimmed:true})],me=[D,["name","name",true,v(A,{required:true,trimmed:true})],["dose","dose",false,v(fe,{trimmed:true})],["frequency","frequency",false,v(fe,{trimmed:true})],["startDate","start_date",false,C],["endDate","end_date",false,C],F],Et={allergies:[D,["allergen","allergen",true,v(A,{required:true,trimmed:true})],["severity","severity",false,b(vt)],F],medications:me,supplements:me,conditions:[D,["conditionName","condition_name",true,v(A,{required:true,trimmed:true})],["diagnosedDate","diagnosed_date",false,C],F]};function Rt(e,t,r){let o=`${e}[${t}]`;if(typeof r!="object"||r===null||Array.isArray(r))return {error:`${o} must be an object`};let i=r,a={};for(let[u,c,f,y]of Et[e]){let m=i[u];if(m==null){if(f)return {error:`${o}.${u} is required`};m===null&&(a[c]=null);continue}let g=y(`${o}.${u}`,m);if(g!==null)return {error:g};a[c]=m;}let l=a.start_date,d=a.end_date;return typeof l=="string"&&typeof d=="string"&&d<l?{error:`${o}.endDate must not be before startDate`}:{item:a}}n(Rt,"itemToWire");function Pt(e,t){if(!Array.isArray(t))return {error:`${e} must be an array (send [] to clear it)`};if(t.length>pe)return {error:`${e} has ${t.length} items; the server accepts at most ${pe}`};let r=[],o=new Set;for(let i=0;i<t.length;i+=1){let a=Rt(e,i,t[i]);if("error"in a)return a;let l=a.item.ref;if(typeof l=="string"){let d=l.trim();if(o.has(d))return {error:`${e}: ref ${JSON.stringify(d)} is used twice; a ref names one item`};o.add(d);}r.push(a.item);}return {items:r}}n(Pt,"listToWire");function _e(e,t){let r=e,o={};for(let[l,d]of ve){let u=r[l];if(u!==void 0){if(u!==null){let c=bt[l](l,u);if(c!==null)return {error:c}}o[d]=u;}}let i=r.birthDate!==void 0,a=r.birthDatePrecision!==void 0;if(i!==a||i&&r.birthDate===null!=(r.birthDatePrecision===null))return {error:"birthDate and birthDatePrecision must be sent together, both set or both null"};for(let[l,d]of ge){let u=r[l];if(u===void 0)continue;let c=Pt(l,u);if("error"in c)return c;o[d]=c.items;}return Object.keys(o).length>0&&t!==void 0&&ce(t)===null?{error:"health fields need a pet Everfur reads as a dog or a cat (dog, canine, k9, puppy, cat, feline, kitten)"}:{body:o}}n(_e,"petHealthToWire");var wt=["sk_partner_","rk_partner_"];function he(e,t){if(typeof e!="string"||e.length===0)throw new s("invalid-argument","partnerSecretKey is required");if(e.startsWith("pk_"))throw new s("invalid-argument",`partnerSecretKey is a publishable key (pk_...): ${t} needs your secret key, server side only`);if(!wt.some(r=>e.startsWith(r)))throw new s("invalid-argument","partnerSecretKey must be an sk_partner_ secret key or an rk_partner_ restricted key")}n(he,"assertServerSecretKey");var St=[["name","name"],["species","species"],["breed","breed"],["ageYears","age_years"],["weightKg","weight_kg"],["partnerLabelInstructions","partner_label_instructions"]];function be(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new s("invalid-argument","the pet profile must be an object");let t={};for(let[o,i]of St)e[o]!==void 0&&(t[i]=e[o]);let r=_e(e,e.species);if("error"in r)throw new s("invalid-argument",r.error);return Object.assign(t,r.body)}n(be,"partnerPetProfileBody");var kt=/^[\x21-\x7e]{1,255}$/,Ee=32*1024,Ot=/^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:[Zz]|[+-]\d{2}:?\d{2})$/,Tt=3e4,xt=Object.freeze({"pet.profile.updated":{subject:"pet",schemaVersions:[1,2]},"pet.incident.reported":{subject:"pet",schemaVersions:[1]},"pet.behavior_note.added":{subject:"pet",schemaVersions:[1]},"pet.service_score.recorded":{subject:"pet",schemaVersions:[1]},"pet.deleted":{subject:"pet",schemaVersions:[1]},"member.status_changed":{subject:"member",schemaVersions:[1]},"member.deleted":{subject:"member",schemaVersions:[1]}});async function It(e,t,r){let o=r??randomUUID();if(typeof o!="string"||!kt.test(o))throw new s("invalid-argument","idempotencyKey must be 1 to 255 visible ASCII characters (no spaces, no control or non-ASCII characters)");he(e.partnerSecretKey,"sending events");let i=Nt(At(t)),a=e.fetchImpl??oe(),l=`${W(e.apiBaseUrl)}/partners/events`,d={...H({sdkVersion:V,platform:B,contractVersion:e.contractVersion??q}),accept:"application/json","content-type":"application/json","idempotency-key":o,"x-everfur-partner-secret-key":e.partnerSecretKey},u=new AbortController,c=setTimeout(()=>u.abort(),Tt),f;try{f=await a(l,{method:"POST",headers:d,body:i,signal:u.signal});}catch(Pe){return _(re(Pe))}finally{clearTimeout(c);}let y=null;try{y=await f.json();}catch{y=null;}let m=ne(f);if(!f.ok)return _(I(f.status,y,m));let g=jt(y,o);return g?k(g):_(I(502,{error:{code:"INTERNAL_ERROR"}},m))}n(It,"sendPartnerEvent");function Re(e){return typeof e=="string"&&w.includes(e)}n(Re,"isInboundType");function At(e){if(typeof e!="object"||e===null)throw new s("invalid-argument","event is required");if(!Re(e.type))throw new s("invalid-argument",`event.type must be one of ${w.join(", ")}`);let t=xt[e.type],r=e.schemaVersion??1;if(typeof r!="number"||!t.schemaVersions.includes(r))throw new s("invalid-argument",`event.schemaVersion must be one of ${t.schemaVersions.join(", ")} for ${e.type}`);let o=S(e.userRef);if(t.subject==="pet"&&e.petRef===void 0)throw new s("invalid-argument",`event.petRef is required for ${e.type}`);if(t.subject==="member"&&e.petRef!==void 0)throw new s("invalid-argument",`event.petRef is not accepted on ${e.type}`);let i=e.petRef===void 0?void 0:Dt(e.petRef);if(typeof e.data!="object"||e.data===null||Array.isArray(e.data))throw new s("invalid-argument","event.data must be an object");let a=e.type==="pet.profile.updated"&&r===2?be(e.data):e.data,l={type:e.type,schema_version:r,user_ref:o,occurred_at:Ft(e.occurredAt),data:a};return i!==void 0&&(l.pet_ref=i),l}n(At,"buildBody");function Dt(e){try{return S(e)}catch(t){throw t instanceof s?new s("invalid-argument",`event.petRef follows the user_ref rule: ${t.message}`):t}}n(Dt,"assertValidPetRef");function Ft(e){if(typeof e=="string"&&!Ot.test(e))throw new s("invalid-argument","event.occurredAt must be a Date or an ISO 8601 date-time with an explicit offset (Z or +05:30)");let t=e instanceof Date?e:typeof e=="string"?new Date(e):new Date(Number.NaN);if(Number.isNaN(t.getTime()))throw new s("invalid-argument","event.occurredAt must be a Date or an ISO 8601 date-time with an explicit offset (Z or +05:30)");return t.toISOString()}n(Ft,"occurredAtIso");function Nt(e){let t;try{t=JSON.stringify(e);}catch{throw new s("invalid-argument","event.data must be JSON-serialisable")}let r=new TextEncoder().encode(t).byteLength;if(r>Ee)throw new s("invalid-argument",`the event is ${r} bytes once serialised; Everfur accepts at most ${Ee} (32 KB)`);return t}n(Nt,"serialize");function jt(e,t){if(typeof e!="object"||e===null)return null;let r=e,o=r.id,i=r.type,a=r.schema_version,l=r.user_ref,d=r.pet_ref??null,u=r.occurred_at,c=r.received_at,f=r.status,y=r.livemode;return typeof o!="string"||o.length===0||!Re(i)||typeof a!="number"||typeof l!="string"||d!==null&&typeof d!="string"||typeof u!="string"||typeof c!="string"||typeof f!="string"||typeof y!="boolean"?null:Object.freeze({id:o,type:i,schemaVersion:a,userRef:l,petRef:d,occurredAt:u,receivedAt:c,status:f,livemode:y,idempotencyKey:t})}n(jt,"parseReceipt");
|
|
2
|
+
export{N as DEFAULT_WEBHOOK_TOLERANCE_SECONDS,ke as EVERFUR_SIGNATURE_HEADER,ie as EVERFUR_WEBHOOK_EVENT_TYPES,it as EVERFUR_WEBHOOK_ONLY_EVENT_TYPES,p as EverfurWebhookVerificationError,w as PARTNER_INBOUND_EVENT_TYPES,Ie as constructEvent,Ae as generateTestSignatureHeader,at as isEverfurWebhookEvent,ae as isEverfurWebhookEventType,It as sendPartnerEvent};
|