@everfur/sdk 0.1.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 +74 -0
- package/LICENSE +21 -0
- package/README.md +161 -0
- package/animations/package.json +8 -0
- package/chat/package.json +8 -0
- package/client/package.json +8 -0
- package/core/package.json +8 -0
- package/dist/CameraPort-Cv31Pz7g.d.cts +29 -0
- package/dist/CameraPort-Cv31Pz7g.d.ts +29 -0
- package/dist/ChatController-CKdBvPj2.d.ts +146 -0
- package/dist/ChatController-CpUMvvZf.d.cts +146 -0
- package/dist/EverfurResult-D92-uL82.d.cts +240 -0
- package/dist/EverfurResult-D92-uL82.d.ts +240 -0
- package/dist/FilePort-BabWrv7I.d.cts +22 -0
- package/dist/FilePort-BabWrv7I.d.ts +22 -0
- package/dist/PhotoController-BItt5M7u.d.cts +43 -0
- package/dist/PhotoController-D8zMTdcW.d.ts +43 -0
- package/dist/TelemetryPort-BDNr00hu.d.cts +12 -0
- package/dist/TelemetryPort-BDNr00hu.d.ts +12 -0
- package/dist/animations/index.cjs +1997 -0
- package/dist/animations/index.d.cts +517 -0
- package/dist/animations/index.d.ts +517 -0
- package/dist/animations/index.js +1972 -0
- package/dist/cacheEpoch-DknKn3S0.d.cts +36 -0
- package/dist/cacheEpoch-DknKn3S0.d.ts +36 -0
- package/dist/chat/index.cjs +1170 -0
- package/dist/chat/index.d.cts +59 -0
- package/dist/chat/index.d.ts +59 -0
- package/dist/chat/index.js +1167 -0
- package/dist/client/index.cjs +2317 -0
- package/dist/client/index.d.cts +65 -0
- package/dist/client/index.d.ts +65 -0
- package/dist/client/index.js +2217 -0
- package/dist/config-BSjBdxrZ.d.cts +501 -0
- package/dist/config-CiJ0PVBB.d.ts +501 -0
- package/dist/core/index.cjs +3175 -0
- package/dist/core/index.d.cts +70 -0
- package/dist/core/index.d.ts +70 -0
- package/dist/core/index.js +3163 -0
- package/dist/identity-Brl-lDd6.d.cts +91 -0
- package/dist/identity-DK9zORrG.d.ts +91 -0
- package/dist/ids-CJ1S6adf.d.cts +46 -0
- package/dist/ids-CJ1S6adf.d.ts +46 -0
- package/dist/index.cjs +4488 -0
- package/dist/index.d.cts +156 -0
- package/dist/index.d.ts +156 -0
- package/dist/index.js +4464 -0
- package/dist/petsRepository-BEGb97M9.d.cts +326 -0
- package/dist/petsRepository-Bu18r2kK.d.ts +326 -0
- package/dist/photo/index.cjs +2189 -0
- package/dist/photo/index.d.cts +43 -0
- package/dist/photo/index.d.ts +43 -0
- package/dist/photo/index.js +2186 -0
- package/dist/projection-CeIUsbSk.d.cts +8 -0
- package/dist/projection-CeIUsbSk.d.ts +8 -0
- package/dist/records/index.cjs +2840 -0
- package/dist/records/index.d.cts +224 -0
- package/dist/records/index.d.ts +224 -0
- package/dist/records/index.js +2834 -0
- package/dist/requestFunnel-DuUH-kAe.d.cts +28 -0
- package/dist/requestFunnel-dio5OmR9.d.ts +28 -0
- package/dist/resolve-Dq_4_agU.d.cts +86 -0
- package/dist/resolve-Dq_4_agU.d.ts +86 -0
- package/dist/runtime-BgQnA594.d.cts +349 -0
- package/dist/runtime-CBA-LvdM.d.ts +349 -0
- package/dist/server/index.cjs +533 -0
- package/dist/server/index.d.cts +48 -0
- package/dist/server/index.d.ts +48 -0
- package/dist/server/index.js +530 -0
- package/dist/testing/index.cjs +825 -0
- package/dist/testing/index.d.cts +113 -0
- package/dist/testing/index.d.ts +113 -0
- package/dist/testing/index.js +822 -0
- package/dist/testing/rn/index.cjs +449 -0
- package/dist/testing/rn/index.d.cts +49 -0
- package/dist/testing/rn/index.d.ts +49 -0
- package/dist/testing/rn/index.js +444 -0
- package/dist/video/index.cjs +1941 -0
- package/dist/video/index.d.cts +66 -0
- package/dist/video/index.d.ts +66 -0
- package/dist/video/index.js +1938 -0
- package/package.json +311 -0
- package/photo/package.json +8 -0
- package/records/package.json +8 -0
- package/server/device-blocked.cjs +15 -0
- package/server/package.json +9 -0
- package/testing/package.json +8 -0
- package/testing/rn/package.json +8 -0
- package/video/package.json +8 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/** The frozen error taxonomy. Additive-only under the dated-contract policy. */
|
|
2
|
+
type EverfurErrorCode = 'authExpired' | 'authRejected' | 'sessionRequired' | 'accessDenied' | 'paymentRequired' | 'quotaExceeded' | 'rateLimited' | 'validationFailed' | 'sdkUpdateRequired' | 'conflict' | 'idempotencyConflict' | 'idempotencyInFlight' | 'conversationNotFound' | 'petNotFound' | 'resourceNotFound' | 'streamTimeout' | 'mediaUploadFailed' | 'moderationBlocked' | 'upstreamLlmTimeout' | 'internalError' | 'notImplemented' | 'serviceUnavailable' | 'streamIncomplete' | 'unknown';
|
|
3
|
+
/** Every code, frozen, for iteration in conformance / property tests. */
|
|
4
|
+
declare const EVERFUR_ERROR_CODES: readonly EverfurErrorCode[];
|
|
5
|
+
/**
|
|
6
|
+
* The coarse failure axis. Adopted from Stripe's error CONTENT (a stable, documented type alongside a
|
|
7
|
+
* fine-grained code) while REFUSING its control flow: an Everfur failure is still a returned
|
|
8
|
+
* `EverfurResult`, never a throw, so a domain failure can never trip a partner's error boundary.
|
|
9
|
+
*
|
|
10
|
+
* Branch on `category` for the shape of the response (re-auth? show a paywall? offer retry?) and on
|
|
11
|
+
* `code` only when the exact case matters. `category` is total over the taxonomy, so a code added in a
|
|
12
|
+
* later SDK still lands in a bucket an already-shipped consumer handles.
|
|
13
|
+
*/
|
|
14
|
+
type EverfurErrorCategory = 'authentication' | 'authorization' | 'entitlement' | 'rateLimit' | 'invalidRequest' | 'notFound' | 'conflict' | 'connection' | 'server' | 'media' | 'policy' | 'unknown';
|
|
15
|
+
/** Every category, frozen, for iteration in conformance / property tests. */
|
|
16
|
+
declare const EVERFUR_ERROR_CATEGORIES: readonly EverfurErrorCategory[];
|
|
17
|
+
/** The coarse category for a normalized code. Total: every member has exactly one category. */
|
|
18
|
+
declare function categoryFor(code: EverfurErrorCode): EverfurErrorCategory;
|
|
19
|
+
/**
|
|
20
|
+
* Map a raw wire `code` (any casing, any channel) to the normalized enum.
|
|
21
|
+
*
|
|
22
|
+
* Order, and why:
|
|
23
|
+
* 1. The CONTRACT REGISTRY first (./contract). Every code `contract/errors.yaml` declares folds to the
|
|
24
|
+
* member that row names. It has to PRECEDE the legacy rules for one code and it CHANGES the answer
|
|
25
|
+
* for sixteen, and those are different statements:
|
|
26
|
+
*
|
|
27
|
+
* - ORDERING. `AuthorizationException` is the only registry code with a non-`unknown` legacy fold.
|
|
28
|
+
* It is the generic, NON-retryable 401 and must fold to `authRejected`, where the blanket `AUTH*`
|
|
29
|
+
* prefix rule below would have folded it to `authExpired` and spun a token refresh against a 401
|
|
30
|
+
* no fresh token can fix. That is the fold the ORDER decides.
|
|
31
|
+
* - COVERAGE. Sixteen codes that previously fell through to `unknown` now resolve to a named
|
|
32
|
+
* member. They are enumerated in `tests/contract/errorRegistry.test.ts` ("every code that used
|
|
33
|
+
* to fall through to `unknown` now has a name"), which is the list to read before assuming this
|
|
34
|
+
* change is cosmetic.
|
|
35
|
+
*
|
|
36
|
+
* TWO OF THOSE SIXTEEN REACH THE AUTOMATIC RETRY LOOP, not just the affordance. `ThrottlingException`
|
|
37
|
+
* now folds to `rateLimited` and `InternalFailureException` to `internalError`, both members of
|
|
38
|
+
* AUTO_RETRYABLE_CODES below. `transport.ts` computes `isAutoRetryable(fromWire(wireCode))` into the
|
|
39
|
+
* synthetic error frame's `retryable`, which `ChatController` reads as `framedRetry` and turns into
|
|
40
|
+
* up to `maxAttempts` retried stream attempts. So a 500 `InternalFailureException` that used to be
|
|
41
|
+
* terminal is now retried with backoff. That widening is DELIBERATE (the registry marks both
|
|
42
|
+
* retryable and the historical failure was a retryable 503 arriving at the UI as a dead end) and it
|
|
43
|
+
* is pinned by a test, so it cannot be walked back by accident.
|
|
44
|
+
* 2. The `AUTH*` prefix fold, kept verbatim for Dart parity (everfur_error_code.dart:43-65).
|
|
45
|
+
* 3. The exact-match switch: wire codes that are live but not (yet) in the registry.
|
|
46
|
+
* 4. Anything else -> `unknown`, which is NON-retryable. A new backend code is absorbed, never fatal.
|
|
47
|
+
*/
|
|
48
|
+
declare function fromWire(wire: string | null): EverfurErrorCode;
|
|
49
|
+
/**
|
|
50
|
+
* HTTP-status fallback (SPEC-00 §3.1), used ONLY when there is no wire `code`.
|
|
51
|
+
* 403 is DISTINCT from auth (accessDenied); it must never sign the user out or nuke a valid session.
|
|
52
|
+
* 421 folds to `unknown` (a misdirected request, the deprecated storefront proxy rejecting a
|
|
53
|
+
* partner key). 400/422 have no dedicated enum member; they surface as `unknown` (NON-retryable).
|
|
54
|
+
*
|
|
55
|
+
* DELIBERATELY unchanged by the registry reconciliation. This is the no-wire-code path, so there is no
|
|
56
|
+
* registry row to consult: a bare 400 could be `ValidationException` or `invalidRequest`, and a bare 503
|
|
57
|
+
* could be `ServiceUnavailableException` or `unavailable`. Guessing one would hand a caller a specific
|
|
58
|
+
* remedy for a failure the server never identified, so the status fallback stays as coarse as the
|
|
59
|
+
* evidence. When a wire code IS present, `fromWire` wins and the specific member is used.
|
|
60
|
+
*/
|
|
61
|
+
declare function fromHttpStatus(status: number): EverfurErrorCode;
|
|
62
|
+
/** The retryable-with-backoff set (SPEC-00 §3.2). Frozen for property tests. */
|
|
63
|
+
declare const AUTO_RETRYABLE_CODES: ReadonlySet<EverfurErrorCode>;
|
|
64
|
+
/**
|
|
65
|
+
* True for the retryable-with-backoff set. `authExpired` is NOT here: it is not a backoff retry, it
|
|
66
|
+
* triggers one token refresh + single replay (§4.3), then fails as `authRejected`.
|
|
67
|
+
*/
|
|
68
|
+
declare function isAutoRetryable(code: EverfurErrorCode): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Codes the CONTRACT REGISTRY marks retryable that the SDK's backoff set does not contain.
|
|
71
|
+
*
|
|
72
|
+
* Two different questions, deliberately kept apart:
|
|
73
|
+
* - `isAutoRetryable` answers "will the SDK silently retry this with backoff?" It is the input to the
|
|
74
|
+
* controller's retry loop and to `defaultErrorPolicy`, and it is FROZEN at its five members.
|
|
75
|
+
* - `isRetryableCode` answers "could retrying this ever succeed?" It is what a surface reads to decide
|
|
76
|
+
* whether to offer a Retry affordance, and it is the union of the backoff set with the registry's own
|
|
77
|
+
* `retryable: true` rows.
|
|
78
|
+
*
|
|
79
|
+
* The split is ADDITIVE-ONLY by construction: this set names only members introduced by the registry
|
|
80
|
+
* reconciliation, so importing the registry's retry semantics cannot re-litigate the policy of a code
|
|
81
|
+
* that already shipped. (`DAILY_QUOTA_EXCEEDED` is the sharp example: the registry marks it retryable,
|
|
82
|
+
* but `quotaExceeded` has shipped as terminal-for-today and keeps that behaviour.)
|
|
83
|
+
*/
|
|
84
|
+
declare const RETRYABLE_CONTRACT_CODES: ReadonlySet<EverfurErrorCode>;
|
|
85
|
+
/**
|
|
86
|
+
* True when retrying could plausibly succeed: the backoff set, plus the registry-declared retryable
|
|
87
|
+
* codes. Drives the retry AFFORDANCE (`EverfurError.retryable`), not the automatic retry loop.
|
|
88
|
+
*/
|
|
89
|
+
declare function isRetryableCode(code: EverfurErrorCode): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* The full retry decision: a frame's explicit `retryable:true` ALSO forces a retry
|
|
92
|
+
* (`shouldRetry = frameRetryable || isAutoRetryable(code)`), parity with EverfurChatException.shouldRetry.
|
|
93
|
+
*/
|
|
94
|
+
declare function shouldRetry(code: EverfurErrorCode, frameRetryable?: boolean): boolean;
|
|
95
|
+
/** Exponential-with-full-jitter base. */
|
|
96
|
+
declare const BACKOFF_BASE_MS = 500;
|
|
97
|
+
/** Exponential-with-full-jitter cap. */
|
|
98
|
+
declare const BACKOFF_CAP_MS = 8000;
|
|
99
|
+
/** Max attempts before surfacing the terminal error. */
|
|
100
|
+
declare const MAX_RETRY_ATTEMPTS = 4;
|
|
101
|
+
/**
|
|
102
|
+
* Full-jitter backoff: `delay = random(0, min(BASE * 2^attempt, CAP))`. `attempt` is 0-based.
|
|
103
|
+
* `rng` is injectable so tests are deterministic.
|
|
104
|
+
*/
|
|
105
|
+
declare function backoffDelayMs(attempt: number, rng?: () => number): number;
|
|
106
|
+
/**
|
|
107
|
+
* 403-excluded-from-recreate (SPEC-00 §3 / §4.3). A 404 `conversationNotFound` recreates the
|
|
108
|
+
* conversation and resends ONCE; a 403 `accessDenied` is TERMINAL, it never recreates a conversation
|
|
109
|
+
* and never invalidates the session (the REACT-NATIVE-12 storm). This predicate is the single source
|
|
110
|
+
* of that rule for the recovery policy: ONLY `conversationNotFound` may trigger a recreate.
|
|
111
|
+
*/
|
|
112
|
+
declare function canRecreateOnNotFound(code: EverfurErrorCode): boolean;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The one normalized error object. Expected domain failures settle as this (inside an EverfurResult or a
|
|
116
|
+
* terminal frame), never a throw. `wireMessage` is telemetry/logs only and is never surfaced to a user.
|
|
117
|
+
*/
|
|
118
|
+
interface EverfurError {
|
|
119
|
+
readonly code: EverfurErrorCode;
|
|
120
|
+
/** Coarse branching axis (auth vs entitlement vs connection ...). Total over the taxonomy. */
|
|
121
|
+
readonly category: EverfurErrorCategory;
|
|
122
|
+
/** `isRetryableCode(code)` OR an explicit `retryable:true` from the frame / body. */
|
|
123
|
+
readonly retryable: boolean;
|
|
124
|
+
readonly httpStatus?: number;
|
|
125
|
+
/**
|
|
126
|
+
* The `X-Request-ID` of the response that produced this failure, when the server sent one. The one
|
|
127
|
+
* string a partner can quote in a support ticket, and the join key back to the server-side trace.
|
|
128
|
+
* Absent on failures with no HTTP response (an offline fetch rejection) and on the SSE frames, which
|
|
129
|
+
* the contract records as carrying no request id at all.
|
|
130
|
+
*/
|
|
131
|
+
readonly requestId?: string;
|
|
132
|
+
/** Site-relative documentation path for the wire code, from the contract registry. No host is implied. */
|
|
133
|
+
readonly docUrl?: string;
|
|
134
|
+
/** raw wire text; telemetry / logs only, NEVER rendered. NON-ENUMERABLE. */
|
|
135
|
+
readonly wireMessage?: string;
|
|
136
|
+
/** Opaque parsed wire payload for debugging (untrusted, unvalidated). NON-ENUMERABLE. */
|
|
137
|
+
readonly raw?: unknown;
|
|
138
|
+
/** SAFE localizable copy resolved from `code` via `userFacingError` (never the raw wireMessage). */
|
|
139
|
+
readonly displayMessage: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The ONLY function the UI may call to render an error. Returns SAFE, code-derived copy; it never returns
|
|
143
|
+
* the untrusted `wireMessage`. Total over the taxonomy (an unmapped code cannot occur at the type level).
|
|
144
|
+
*/
|
|
145
|
+
declare function userFacingError(code: EverfurErrorCode): string;
|
|
146
|
+
/** Options accepted by `makeEverfurError`. Every field is optional; the code alone is a valid error. */
|
|
147
|
+
interface MakeEverfurErrorOptions {
|
|
148
|
+
/** Forces the retry affordance. Omit to derive it from the code (`isRetryableCode`). */
|
|
149
|
+
readonly retryable?: boolean;
|
|
150
|
+
readonly httpStatus?: number;
|
|
151
|
+
/** `X-Request-ID` from the response, when the server sent one. */
|
|
152
|
+
readonly requestId?: string | null;
|
|
153
|
+
/**
|
|
154
|
+
* The RAW wire code string, used to resolve the contract registry row (and from it, `docUrl`). Pass
|
|
155
|
+
* the code exactly as the server sent it; a code that is not in the registry simply yields no docUrl.
|
|
156
|
+
*/
|
|
157
|
+
readonly wireCode?: string | null;
|
|
158
|
+
/** Untrusted wire text. Carried NON-ENUMERABLY for telemetry; never rendered. */
|
|
159
|
+
readonly wireMessage?: string | null;
|
|
160
|
+
/** Opaque parsed wire payload. Carried NON-ENUMERABLY for debugging; never rendered. */
|
|
161
|
+
readonly raw?: unknown;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Build a normalized, frozen `EverfurError` from a normalized code plus optional wire/HTTP context.
|
|
165
|
+
*
|
|
166
|
+
* `displayMessage` is always the SAFE code-derived string. `retryable` defaults to the code's policy
|
|
167
|
+
* (`isRetryableCode`) unless the caller overrides it, e.g. a frame carrying explicit `retryable:true`.
|
|
168
|
+
* `requestId` and `docUrl` are the two support-facing additions: the id a partner quotes, and the
|
|
169
|
+
* site-relative registry path for the wire code. `wireMessage` and `raw` are carried NON-ENUMERABLY and
|
|
170
|
+
* are dropped when empty.
|
|
171
|
+
*/
|
|
172
|
+
declare function makeEverfurError(code: EverfurErrorCode, opts?: MakeEverfurErrorOptions): EverfurError;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The settle type. An async public operation returns this instead of throwing for an EXPECTED failure.
|
|
176
|
+
* A successful result may carry a non-fatal `warning` (e.g. served-stale, degraded path).
|
|
177
|
+
*/
|
|
178
|
+
type EverfurResult<T> = {
|
|
179
|
+
readonly ok: true;
|
|
180
|
+
readonly value: T;
|
|
181
|
+
readonly warning?: EverfurError;
|
|
182
|
+
} | {
|
|
183
|
+
readonly ok: false;
|
|
184
|
+
readonly error: EverfurError;
|
|
185
|
+
};
|
|
186
|
+
/** Construct a success result. Frozen, public state is immutable end to end. */
|
|
187
|
+
declare function ok<T>(value: T, warning?: EverfurError): EverfurResult<T>;
|
|
188
|
+
/** Construct a failure result. Frozen. */
|
|
189
|
+
declare function err<T = never>(error: EverfurError): EverfurResult<T>;
|
|
190
|
+
/** Narrowing guard: true iff the result is a success. */
|
|
191
|
+
declare function isOk<T>(r: EverfurResult<T>): r is {
|
|
192
|
+
readonly ok: true;
|
|
193
|
+
readonly value: T;
|
|
194
|
+
readonly warning?: EverfurError;
|
|
195
|
+
};
|
|
196
|
+
/** Narrowing guard: true iff the result is a failure. */
|
|
197
|
+
declare function isErr<T>(r: EverfurResult<T>): r is {
|
|
198
|
+
readonly ok: false;
|
|
199
|
+
readonly error: EverfurError;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* The real `Error` subclass `unwrap` throws, so an imperative call site that lets the throw escape
|
|
203
|
+
* produces a proper Error rather than a bare object (which a crash reporter files as
|
|
204
|
+
* "Non-Error exception captured", with no stack and no grouping).
|
|
205
|
+
*
|
|
206
|
+
* `message` is built from the normalized `code` and the frozen, product-owned `displayMessage` ONLY.
|
|
207
|
+
* The untrusted `wireMessage` is deliberately NOT in it: `message` is the one field every telemetry
|
|
208
|
+
* vendor captures and indexes by default, so putting wire text there would make an untrusted,
|
|
209
|
+
* healthcare-adjacent string flow automatically into a partner's third-party tooling. The full
|
|
210
|
+
* `EverfurError` hangs off `.error`, where `wireMessage` and `raw` remain non-enumerable.
|
|
211
|
+
*/
|
|
212
|
+
declare class EverfurThrownError extends Error {
|
|
213
|
+
/** The settled error this was thrown from. Its `wireMessage` / `raw` stay non-enumerable. */
|
|
214
|
+
readonly error: EverfurError;
|
|
215
|
+
/** Mirror of `error.code`, so a catch block can branch without reaching through `.error`. */
|
|
216
|
+
readonly code: EverfurError['code'];
|
|
217
|
+
/** Mirror of `error.category`, the coarse branching axis. */
|
|
218
|
+
readonly category: EverfurError['category'];
|
|
219
|
+
constructor(error: EverfurError);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Escape hatch for imperative callers: return the value or throw. The throw is an `EverfurThrownError`
|
|
223
|
+
* (a real `Error`, safe message); the settled `EverfurError` is on its `.error`.
|
|
224
|
+
* Prefer pattern-matching on `ok`; `unwrap` exists for call sites that want the throw.
|
|
225
|
+
*/
|
|
226
|
+
declare function unwrap<T>(r: EverfurResult<T>): T;
|
|
227
|
+
/**
|
|
228
|
+
* Thrown SYNCHRONOUSLY for programmer error ONLY, never for a network or domain failure.
|
|
229
|
+
* Mirrors Flutter `_requireInitialized` (a real guard, not an assert).
|
|
230
|
+
*
|
|
231
|
+
* - `not-initialized` , a capability used before EverfurProvider mounted / init ran.
|
|
232
|
+
* - `invalid-argument` , a malformed id or option shape (e.g. a non-UUID conversation id).
|
|
233
|
+
* - `missing-peer-dependency`, a capability subpath used without its required native peer dep.
|
|
234
|
+
*/
|
|
235
|
+
declare class EverfurConfigError extends Error {
|
|
236
|
+
readonly kind: 'not-initialized' | 'invalid-argument' | 'missing-peer-dependency';
|
|
237
|
+
constructor(kind: 'not-initialized' | 'invalid-argument' | 'missing-peer-dependency', message: string);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export { AUTO_RETRYABLE_CODES as A, BACKOFF_BASE_MS as B, type EverfurResult as E, MAX_RETRY_ATTEMPTS as M, RETRYABLE_CONTRACT_CODES as R, type EverfurErrorCode as a, BACKOFF_CAP_MS as b, EVERFUR_ERROR_CATEGORIES as c, EVERFUR_ERROR_CODES as d, EverfurConfigError as e, type EverfurError as f, type EverfurErrorCategory as g, EverfurThrownError as h, type MakeEverfurErrorOptions as i, backoffDelayMs as j, canRecreateOnNotFound as k, categoryFor as l, err as m, fromHttpStatus as n, fromWire as o, isAutoRetryable as p, isErr as q, isOk as r, isRetryableCode as s, makeEverfurError as t, ok as u, shouldRetry as v, unwrap as w, userFacingError as x };
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/** The frozen error taxonomy. Additive-only under the dated-contract policy. */
|
|
2
|
+
type EverfurErrorCode = 'authExpired' | 'authRejected' | 'sessionRequired' | 'accessDenied' | 'paymentRequired' | 'quotaExceeded' | 'rateLimited' | 'validationFailed' | 'sdkUpdateRequired' | 'conflict' | 'idempotencyConflict' | 'idempotencyInFlight' | 'conversationNotFound' | 'petNotFound' | 'resourceNotFound' | 'streamTimeout' | 'mediaUploadFailed' | 'moderationBlocked' | 'upstreamLlmTimeout' | 'internalError' | 'notImplemented' | 'serviceUnavailable' | 'streamIncomplete' | 'unknown';
|
|
3
|
+
/** Every code, frozen, for iteration in conformance / property tests. */
|
|
4
|
+
declare const EVERFUR_ERROR_CODES: readonly EverfurErrorCode[];
|
|
5
|
+
/**
|
|
6
|
+
* The coarse failure axis. Adopted from Stripe's error CONTENT (a stable, documented type alongside a
|
|
7
|
+
* fine-grained code) while REFUSING its control flow: an Everfur failure is still a returned
|
|
8
|
+
* `EverfurResult`, never a throw, so a domain failure can never trip a partner's error boundary.
|
|
9
|
+
*
|
|
10
|
+
* Branch on `category` for the shape of the response (re-auth? show a paywall? offer retry?) and on
|
|
11
|
+
* `code` only when the exact case matters. `category` is total over the taxonomy, so a code added in a
|
|
12
|
+
* later SDK still lands in a bucket an already-shipped consumer handles.
|
|
13
|
+
*/
|
|
14
|
+
type EverfurErrorCategory = 'authentication' | 'authorization' | 'entitlement' | 'rateLimit' | 'invalidRequest' | 'notFound' | 'conflict' | 'connection' | 'server' | 'media' | 'policy' | 'unknown';
|
|
15
|
+
/** Every category, frozen, for iteration in conformance / property tests. */
|
|
16
|
+
declare const EVERFUR_ERROR_CATEGORIES: readonly EverfurErrorCategory[];
|
|
17
|
+
/** The coarse category for a normalized code. Total: every member has exactly one category. */
|
|
18
|
+
declare function categoryFor(code: EverfurErrorCode): EverfurErrorCategory;
|
|
19
|
+
/**
|
|
20
|
+
* Map a raw wire `code` (any casing, any channel) to the normalized enum.
|
|
21
|
+
*
|
|
22
|
+
* Order, and why:
|
|
23
|
+
* 1. The CONTRACT REGISTRY first (./contract). Every code `contract/errors.yaml` declares folds to the
|
|
24
|
+
* member that row names. It has to PRECEDE the legacy rules for one code and it CHANGES the answer
|
|
25
|
+
* for sixteen, and those are different statements:
|
|
26
|
+
*
|
|
27
|
+
* - ORDERING. `AuthorizationException` is the only registry code with a non-`unknown` legacy fold.
|
|
28
|
+
* It is the generic, NON-retryable 401 and must fold to `authRejected`, where the blanket `AUTH*`
|
|
29
|
+
* prefix rule below would have folded it to `authExpired` and spun a token refresh against a 401
|
|
30
|
+
* no fresh token can fix. That is the fold the ORDER decides.
|
|
31
|
+
* - COVERAGE. Sixteen codes that previously fell through to `unknown` now resolve to a named
|
|
32
|
+
* member. They are enumerated in `tests/contract/errorRegistry.test.ts` ("every code that used
|
|
33
|
+
* to fall through to `unknown` now has a name"), which is the list to read before assuming this
|
|
34
|
+
* change is cosmetic.
|
|
35
|
+
*
|
|
36
|
+
* TWO OF THOSE SIXTEEN REACH THE AUTOMATIC RETRY LOOP, not just the affordance. `ThrottlingException`
|
|
37
|
+
* now folds to `rateLimited` and `InternalFailureException` to `internalError`, both members of
|
|
38
|
+
* AUTO_RETRYABLE_CODES below. `transport.ts` computes `isAutoRetryable(fromWire(wireCode))` into the
|
|
39
|
+
* synthetic error frame's `retryable`, which `ChatController` reads as `framedRetry` and turns into
|
|
40
|
+
* up to `maxAttempts` retried stream attempts. So a 500 `InternalFailureException` that used to be
|
|
41
|
+
* terminal is now retried with backoff. That widening is DELIBERATE (the registry marks both
|
|
42
|
+
* retryable and the historical failure was a retryable 503 arriving at the UI as a dead end) and it
|
|
43
|
+
* is pinned by a test, so it cannot be walked back by accident.
|
|
44
|
+
* 2. The `AUTH*` prefix fold, kept verbatim for Dart parity (everfur_error_code.dart:43-65).
|
|
45
|
+
* 3. The exact-match switch: wire codes that are live but not (yet) in the registry.
|
|
46
|
+
* 4. Anything else -> `unknown`, which is NON-retryable. A new backend code is absorbed, never fatal.
|
|
47
|
+
*/
|
|
48
|
+
declare function fromWire(wire: string | null): EverfurErrorCode;
|
|
49
|
+
/**
|
|
50
|
+
* HTTP-status fallback (SPEC-00 §3.1), used ONLY when there is no wire `code`.
|
|
51
|
+
* 403 is DISTINCT from auth (accessDenied); it must never sign the user out or nuke a valid session.
|
|
52
|
+
* 421 folds to `unknown` (a misdirected request, the deprecated storefront proxy rejecting a
|
|
53
|
+
* partner key). 400/422 have no dedicated enum member; they surface as `unknown` (NON-retryable).
|
|
54
|
+
*
|
|
55
|
+
* DELIBERATELY unchanged by the registry reconciliation. This is the no-wire-code path, so there is no
|
|
56
|
+
* registry row to consult: a bare 400 could be `ValidationException` or `invalidRequest`, and a bare 503
|
|
57
|
+
* could be `ServiceUnavailableException` or `unavailable`. Guessing one would hand a caller a specific
|
|
58
|
+
* remedy for a failure the server never identified, so the status fallback stays as coarse as the
|
|
59
|
+
* evidence. When a wire code IS present, `fromWire` wins and the specific member is used.
|
|
60
|
+
*/
|
|
61
|
+
declare function fromHttpStatus(status: number): EverfurErrorCode;
|
|
62
|
+
/** The retryable-with-backoff set (SPEC-00 §3.2). Frozen for property tests. */
|
|
63
|
+
declare const AUTO_RETRYABLE_CODES: ReadonlySet<EverfurErrorCode>;
|
|
64
|
+
/**
|
|
65
|
+
* True for the retryable-with-backoff set. `authExpired` is NOT here: it is not a backoff retry, it
|
|
66
|
+
* triggers one token refresh + single replay (§4.3), then fails as `authRejected`.
|
|
67
|
+
*/
|
|
68
|
+
declare function isAutoRetryable(code: EverfurErrorCode): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Codes the CONTRACT REGISTRY marks retryable that the SDK's backoff set does not contain.
|
|
71
|
+
*
|
|
72
|
+
* Two different questions, deliberately kept apart:
|
|
73
|
+
* - `isAutoRetryable` answers "will the SDK silently retry this with backoff?" It is the input to the
|
|
74
|
+
* controller's retry loop and to `defaultErrorPolicy`, and it is FROZEN at its five members.
|
|
75
|
+
* - `isRetryableCode` answers "could retrying this ever succeed?" It is what a surface reads to decide
|
|
76
|
+
* whether to offer a Retry affordance, and it is the union of the backoff set with the registry's own
|
|
77
|
+
* `retryable: true` rows.
|
|
78
|
+
*
|
|
79
|
+
* The split is ADDITIVE-ONLY by construction: this set names only members introduced by the registry
|
|
80
|
+
* reconciliation, so importing the registry's retry semantics cannot re-litigate the policy of a code
|
|
81
|
+
* that already shipped. (`DAILY_QUOTA_EXCEEDED` is the sharp example: the registry marks it retryable,
|
|
82
|
+
* but `quotaExceeded` has shipped as terminal-for-today and keeps that behaviour.)
|
|
83
|
+
*/
|
|
84
|
+
declare const RETRYABLE_CONTRACT_CODES: ReadonlySet<EverfurErrorCode>;
|
|
85
|
+
/**
|
|
86
|
+
* True when retrying could plausibly succeed: the backoff set, plus the registry-declared retryable
|
|
87
|
+
* codes. Drives the retry AFFORDANCE (`EverfurError.retryable`), not the automatic retry loop.
|
|
88
|
+
*/
|
|
89
|
+
declare function isRetryableCode(code: EverfurErrorCode): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* The full retry decision: a frame's explicit `retryable:true` ALSO forces a retry
|
|
92
|
+
* (`shouldRetry = frameRetryable || isAutoRetryable(code)`), parity with EverfurChatException.shouldRetry.
|
|
93
|
+
*/
|
|
94
|
+
declare function shouldRetry(code: EverfurErrorCode, frameRetryable?: boolean): boolean;
|
|
95
|
+
/** Exponential-with-full-jitter base. */
|
|
96
|
+
declare const BACKOFF_BASE_MS = 500;
|
|
97
|
+
/** Exponential-with-full-jitter cap. */
|
|
98
|
+
declare const BACKOFF_CAP_MS = 8000;
|
|
99
|
+
/** Max attempts before surfacing the terminal error. */
|
|
100
|
+
declare const MAX_RETRY_ATTEMPTS = 4;
|
|
101
|
+
/**
|
|
102
|
+
* Full-jitter backoff: `delay = random(0, min(BASE * 2^attempt, CAP))`. `attempt` is 0-based.
|
|
103
|
+
* `rng` is injectable so tests are deterministic.
|
|
104
|
+
*/
|
|
105
|
+
declare function backoffDelayMs(attempt: number, rng?: () => number): number;
|
|
106
|
+
/**
|
|
107
|
+
* 403-excluded-from-recreate (SPEC-00 §3 / §4.3). A 404 `conversationNotFound` recreates the
|
|
108
|
+
* conversation and resends ONCE; a 403 `accessDenied` is TERMINAL, it never recreates a conversation
|
|
109
|
+
* and never invalidates the session (the REACT-NATIVE-12 storm). This predicate is the single source
|
|
110
|
+
* of that rule for the recovery policy: ONLY `conversationNotFound` may trigger a recreate.
|
|
111
|
+
*/
|
|
112
|
+
declare function canRecreateOnNotFound(code: EverfurErrorCode): boolean;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The one normalized error object. Expected domain failures settle as this (inside an EverfurResult or a
|
|
116
|
+
* terminal frame), never a throw. `wireMessage` is telemetry/logs only and is never surfaced to a user.
|
|
117
|
+
*/
|
|
118
|
+
interface EverfurError {
|
|
119
|
+
readonly code: EverfurErrorCode;
|
|
120
|
+
/** Coarse branching axis (auth vs entitlement vs connection ...). Total over the taxonomy. */
|
|
121
|
+
readonly category: EverfurErrorCategory;
|
|
122
|
+
/** `isRetryableCode(code)` OR an explicit `retryable:true` from the frame / body. */
|
|
123
|
+
readonly retryable: boolean;
|
|
124
|
+
readonly httpStatus?: number;
|
|
125
|
+
/**
|
|
126
|
+
* The `X-Request-ID` of the response that produced this failure, when the server sent one. The one
|
|
127
|
+
* string a partner can quote in a support ticket, and the join key back to the server-side trace.
|
|
128
|
+
* Absent on failures with no HTTP response (an offline fetch rejection) and on the SSE frames, which
|
|
129
|
+
* the contract records as carrying no request id at all.
|
|
130
|
+
*/
|
|
131
|
+
readonly requestId?: string;
|
|
132
|
+
/** Site-relative documentation path for the wire code, from the contract registry. No host is implied. */
|
|
133
|
+
readonly docUrl?: string;
|
|
134
|
+
/** raw wire text; telemetry / logs only, NEVER rendered. NON-ENUMERABLE. */
|
|
135
|
+
readonly wireMessage?: string;
|
|
136
|
+
/** Opaque parsed wire payload for debugging (untrusted, unvalidated). NON-ENUMERABLE. */
|
|
137
|
+
readonly raw?: unknown;
|
|
138
|
+
/** SAFE localizable copy resolved from `code` via `userFacingError` (never the raw wireMessage). */
|
|
139
|
+
readonly displayMessage: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The ONLY function the UI may call to render an error. Returns SAFE, code-derived copy; it never returns
|
|
143
|
+
* the untrusted `wireMessage`. Total over the taxonomy (an unmapped code cannot occur at the type level).
|
|
144
|
+
*/
|
|
145
|
+
declare function userFacingError(code: EverfurErrorCode): string;
|
|
146
|
+
/** Options accepted by `makeEverfurError`. Every field is optional; the code alone is a valid error. */
|
|
147
|
+
interface MakeEverfurErrorOptions {
|
|
148
|
+
/** Forces the retry affordance. Omit to derive it from the code (`isRetryableCode`). */
|
|
149
|
+
readonly retryable?: boolean;
|
|
150
|
+
readonly httpStatus?: number;
|
|
151
|
+
/** `X-Request-ID` from the response, when the server sent one. */
|
|
152
|
+
readonly requestId?: string | null;
|
|
153
|
+
/**
|
|
154
|
+
* The RAW wire code string, used to resolve the contract registry row (and from it, `docUrl`). Pass
|
|
155
|
+
* the code exactly as the server sent it; a code that is not in the registry simply yields no docUrl.
|
|
156
|
+
*/
|
|
157
|
+
readonly wireCode?: string | null;
|
|
158
|
+
/** Untrusted wire text. Carried NON-ENUMERABLY for telemetry; never rendered. */
|
|
159
|
+
readonly wireMessage?: string | null;
|
|
160
|
+
/** Opaque parsed wire payload. Carried NON-ENUMERABLY for debugging; never rendered. */
|
|
161
|
+
readonly raw?: unknown;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Build a normalized, frozen `EverfurError` from a normalized code plus optional wire/HTTP context.
|
|
165
|
+
*
|
|
166
|
+
* `displayMessage` is always the SAFE code-derived string. `retryable` defaults to the code's policy
|
|
167
|
+
* (`isRetryableCode`) unless the caller overrides it, e.g. a frame carrying explicit `retryable:true`.
|
|
168
|
+
* `requestId` and `docUrl` are the two support-facing additions: the id a partner quotes, and the
|
|
169
|
+
* site-relative registry path for the wire code. `wireMessage` and `raw` are carried NON-ENUMERABLY and
|
|
170
|
+
* are dropped when empty.
|
|
171
|
+
*/
|
|
172
|
+
declare function makeEverfurError(code: EverfurErrorCode, opts?: MakeEverfurErrorOptions): EverfurError;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The settle type. An async public operation returns this instead of throwing for an EXPECTED failure.
|
|
176
|
+
* A successful result may carry a non-fatal `warning` (e.g. served-stale, degraded path).
|
|
177
|
+
*/
|
|
178
|
+
type EverfurResult<T> = {
|
|
179
|
+
readonly ok: true;
|
|
180
|
+
readonly value: T;
|
|
181
|
+
readonly warning?: EverfurError;
|
|
182
|
+
} | {
|
|
183
|
+
readonly ok: false;
|
|
184
|
+
readonly error: EverfurError;
|
|
185
|
+
};
|
|
186
|
+
/** Construct a success result. Frozen, public state is immutable end to end. */
|
|
187
|
+
declare function ok<T>(value: T, warning?: EverfurError): EverfurResult<T>;
|
|
188
|
+
/** Construct a failure result. Frozen. */
|
|
189
|
+
declare function err<T = never>(error: EverfurError): EverfurResult<T>;
|
|
190
|
+
/** Narrowing guard: true iff the result is a success. */
|
|
191
|
+
declare function isOk<T>(r: EverfurResult<T>): r is {
|
|
192
|
+
readonly ok: true;
|
|
193
|
+
readonly value: T;
|
|
194
|
+
readonly warning?: EverfurError;
|
|
195
|
+
};
|
|
196
|
+
/** Narrowing guard: true iff the result is a failure. */
|
|
197
|
+
declare function isErr<T>(r: EverfurResult<T>): r is {
|
|
198
|
+
readonly ok: false;
|
|
199
|
+
readonly error: EverfurError;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* The real `Error` subclass `unwrap` throws, so an imperative call site that lets the throw escape
|
|
203
|
+
* produces a proper Error rather than a bare object (which a crash reporter files as
|
|
204
|
+
* "Non-Error exception captured", with no stack and no grouping).
|
|
205
|
+
*
|
|
206
|
+
* `message` is built from the normalized `code` and the frozen, product-owned `displayMessage` ONLY.
|
|
207
|
+
* The untrusted `wireMessage` is deliberately NOT in it: `message` is the one field every telemetry
|
|
208
|
+
* vendor captures and indexes by default, so putting wire text there would make an untrusted,
|
|
209
|
+
* healthcare-adjacent string flow automatically into a partner's third-party tooling. The full
|
|
210
|
+
* `EverfurError` hangs off `.error`, where `wireMessage` and `raw` remain non-enumerable.
|
|
211
|
+
*/
|
|
212
|
+
declare class EverfurThrownError extends Error {
|
|
213
|
+
/** The settled error this was thrown from. Its `wireMessage` / `raw` stay non-enumerable. */
|
|
214
|
+
readonly error: EverfurError;
|
|
215
|
+
/** Mirror of `error.code`, so a catch block can branch without reaching through `.error`. */
|
|
216
|
+
readonly code: EverfurError['code'];
|
|
217
|
+
/** Mirror of `error.category`, the coarse branching axis. */
|
|
218
|
+
readonly category: EverfurError['category'];
|
|
219
|
+
constructor(error: EverfurError);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Escape hatch for imperative callers: return the value or throw. The throw is an `EverfurThrownError`
|
|
223
|
+
* (a real `Error`, safe message); the settled `EverfurError` is on its `.error`.
|
|
224
|
+
* Prefer pattern-matching on `ok`; `unwrap` exists for call sites that want the throw.
|
|
225
|
+
*/
|
|
226
|
+
declare function unwrap<T>(r: EverfurResult<T>): T;
|
|
227
|
+
/**
|
|
228
|
+
* Thrown SYNCHRONOUSLY for programmer error ONLY, never for a network or domain failure.
|
|
229
|
+
* Mirrors Flutter `_requireInitialized` (a real guard, not an assert).
|
|
230
|
+
*
|
|
231
|
+
* - `not-initialized` , a capability used before EverfurProvider mounted / init ran.
|
|
232
|
+
* - `invalid-argument` , a malformed id or option shape (e.g. a non-UUID conversation id).
|
|
233
|
+
* - `missing-peer-dependency`, a capability subpath used without its required native peer dep.
|
|
234
|
+
*/
|
|
235
|
+
declare class EverfurConfigError extends Error {
|
|
236
|
+
readonly kind: 'not-initialized' | 'invalid-argument' | 'missing-peer-dependency';
|
|
237
|
+
constructor(kind: 'not-initialized' | 'invalid-argument' | 'missing-peer-dependency', message: string);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export { AUTO_RETRYABLE_CODES as A, BACKOFF_BASE_MS as B, type EverfurResult as E, MAX_RETRY_ATTEMPTS as M, RETRYABLE_CONTRACT_CODES as R, type EverfurErrorCode as a, BACKOFF_CAP_MS as b, EVERFUR_ERROR_CATEGORIES as c, EVERFUR_ERROR_CODES as d, EverfurConfigError as e, type EverfurError as f, type EverfurErrorCategory as g, EverfurThrownError as h, type MakeEverfurErrorOptions as i, backoffDelayMs as j, canRecreateOnNotFound as k, categoryFor as l, err as m, fromHttpStatus as n, fromWire as o, isAutoRetryable as p, isErr as q, isOk as r, isRetryableCode as s, makeEverfurError as t, ok as u, shouldRetry as v, unwrap as w, userFacingError as x };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Opaque handle to a local file to upload. Server-owned upload target is defined by records/video specs. */
|
|
2
|
+
interface FileHandle {
|
|
3
|
+
/** Local file URI. */
|
|
4
|
+
readonly uri: string;
|
|
5
|
+
readonly mimeType: string;
|
|
6
|
+
readonly sizeBytes?: number;
|
|
7
|
+
/** Original file name, when the host has one. */
|
|
8
|
+
readonly name?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Presigned PUT upload. `idempotencyKey` is reused on every retry (a re-uploaded identical file dedupes
|
|
12
|
+
* server-side). `signal` cancels an in-flight upload. Settles at the controller; this port itself may reject,
|
|
13
|
+
* which the controller normalizes to a typed EverfurError.
|
|
14
|
+
*/
|
|
15
|
+
interface FilePort {
|
|
16
|
+
presignedPut(file: FileHandle, opts: {
|
|
17
|
+
readonly idempotencyKey: string;
|
|
18
|
+
readonly signal?: AbortSignal;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type { FileHandle as F, FilePort as a };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Opaque handle to a local file to upload. Server-owned upload target is defined by records/video specs. */
|
|
2
|
+
interface FileHandle {
|
|
3
|
+
/** Local file URI. */
|
|
4
|
+
readonly uri: string;
|
|
5
|
+
readonly mimeType: string;
|
|
6
|
+
readonly sizeBytes?: number;
|
|
7
|
+
/** Original file name, when the host has one. */
|
|
8
|
+
readonly name?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Presigned PUT upload. `idempotencyKey` is reused on every retry (a re-uploaded identical file dedupes
|
|
12
|
+
* server-side). `signal` cancels an in-flight upload. Settles at the controller; this port itself may reject,
|
|
13
|
+
* which the controller normalizes to a typed EverfurError.
|
|
14
|
+
*/
|
|
15
|
+
interface FilePort {
|
|
16
|
+
presignedPut(file: FileHandle, opts: {
|
|
17
|
+
readonly idempotencyKey: string;
|
|
18
|
+
readonly signal?: AbortSignal;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type { FileHandle as F, FilePort as a };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { J as JobId } from './ids-CJ1S6adf.cjs';
|
|
2
|
+
import { I as Interpretation } from './projection-CeIUsbSk.cjs';
|
|
3
|
+
|
|
4
|
+
/** Deterministic + semantic precheck reason codes (ground truth `everfur_common/schemas/analysis.py`). */
|
|
5
|
+
type PrecheckReasonCode = 'blurry' | 'too_dark' | 'too_bright' | 'too_far' | 'too_close' | 'bad_framing' | 'low_quality' | 'wrong_modality' | 'wrong_species' | 'not_an_animal' | string;
|
|
6
|
+
|
|
7
|
+
/** v1 server models are skin + eye only; ear/teeth resolve to OffState('version') until their models ship. */
|
|
8
|
+
type PhotoRegion = 'skin' | 'eye';
|
|
9
|
+
type RiskTier = 'low_risk' | 'concern' | 'uncertain';
|
|
10
|
+
type ConfidenceTier = 'possible' | 'probable';
|
|
11
|
+
/** Projector-safe only (§7). Mirrors the 14-key allowlist; NO operator internals. */
|
|
12
|
+
interface PhotoAnalysisResult {
|
|
13
|
+
readonly jobId: JobId | null;
|
|
14
|
+
readonly analysisType: PhotoRegion;
|
|
15
|
+
readonly primaryLabel: string;
|
|
16
|
+
readonly riskTier: RiskTier | null;
|
|
17
|
+
readonly confidence: number | null;
|
|
18
|
+
/** Consumer-friendly; render VERBATIM (never subtype_label). */
|
|
19
|
+
readonly subtypeDisplay: string | null;
|
|
20
|
+
/** BE-owned hedge; render verbatim. */
|
|
21
|
+
readonly confidenceTier: ConfidenceTier | null;
|
|
22
|
+
readonly diseaseName: string | null;
|
|
23
|
+
/** Self-contained sentence; the FE owns the hedge prefix. */
|
|
24
|
+
readonly diseaseFraming: string | null;
|
|
25
|
+
readonly copyShort: string | null;
|
|
26
|
+
readonly interpretation: Interpretation | null;
|
|
27
|
+
readonly modelVersion: string | null;
|
|
28
|
+
}
|
|
29
|
+
/** The honest, non-error outcomes of the species/quality gates (§6, §9). NOT an EverfurError. */
|
|
30
|
+
type PhotoGateOutcome = {
|
|
31
|
+
readonly kind: 'accepted';
|
|
32
|
+
} | {
|
|
33
|
+
readonly kind: 'pet_context_required';
|
|
34
|
+
} | {
|
|
35
|
+
readonly kind: 'species_unsupported';
|
|
36
|
+
readonly species: 'cat';
|
|
37
|
+
} | {
|
|
38
|
+
readonly kind: 'retake';
|
|
39
|
+
readonly reasons: readonly PrecheckReasonCode[];
|
|
40
|
+
};
|
|
41
|
+
type PhotoStatus = 'idle' | 'capturing' | 'analyzing' | 'complete' | 'error';
|
|
42
|
+
|
|
43
|
+
export type { ConfidenceTier as C, PhotoStatus as P, RiskTier as R, PhotoRegion as a, PhotoAnalysisResult as b, PhotoGateOutcome as c, PrecheckReasonCode as d };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { J as JobId } from './ids-CJ1S6adf.js';
|
|
2
|
+
import { I as Interpretation } from './projection-CeIUsbSk.js';
|
|
3
|
+
|
|
4
|
+
/** Deterministic + semantic precheck reason codes (ground truth `everfur_common/schemas/analysis.py`). */
|
|
5
|
+
type PrecheckReasonCode = 'blurry' | 'too_dark' | 'too_bright' | 'too_far' | 'too_close' | 'bad_framing' | 'low_quality' | 'wrong_modality' | 'wrong_species' | 'not_an_animal' | string;
|
|
6
|
+
|
|
7
|
+
/** v1 server models are skin + eye only; ear/teeth resolve to OffState('version') until their models ship. */
|
|
8
|
+
type PhotoRegion = 'skin' | 'eye';
|
|
9
|
+
type RiskTier = 'low_risk' | 'concern' | 'uncertain';
|
|
10
|
+
type ConfidenceTier = 'possible' | 'probable';
|
|
11
|
+
/** Projector-safe only (§7). Mirrors the 14-key allowlist; NO operator internals. */
|
|
12
|
+
interface PhotoAnalysisResult {
|
|
13
|
+
readonly jobId: JobId | null;
|
|
14
|
+
readonly analysisType: PhotoRegion;
|
|
15
|
+
readonly primaryLabel: string;
|
|
16
|
+
readonly riskTier: RiskTier | null;
|
|
17
|
+
readonly confidence: number | null;
|
|
18
|
+
/** Consumer-friendly; render VERBATIM (never subtype_label). */
|
|
19
|
+
readonly subtypeDisplay: string | null;
|
|
20
|
+
/** BE-owned hedge; render verbatim. */
|
|
21
|
+
readonly confidenceTier: ConfidenceTier | null;
|
|
22
|
+
readonly diseaseName: string | null;
|
|
23
|
+
/** Self-contained sentence; the FE owns the hedge prefix. */
|
|
24
|
+
readonly diseaseFraming: string | null;
|
|
25
|
+
readonly copyShort: string | null;
|
|
26
|
+
readonly interpretation: Interpretation | null;
|
|
27
|
+
readonly modelVersion: string | null;
|
|
28
|
+
}
|
|
29
|
+
/** The honest, non-error outcomes of the species/quality gates (§6, §9). NOT an EverfurError. */
|
|
30
|
+
type PhotoGateOutcome = {
|
|
31
|
+
readonly kind: 'accepted';
|
|
32
|
+
} | {
|
|
33
|
+
readonly kind: 'pet_context_required';
|
|
34
|
+
} | {
|
|
35
|
+
readonly kind: 'species_unsupported';
|
|
36
|
+
readonly species: 'cat';
|
|
37
|
+
} | {
|
|
38
|
+
readonly kind: 'retake';
|
|
39
|
+
readonly reasons: readonly PrecheckReasonCode[];
|
|
40
|
+
};
|
|
41
|
+
type PhotoStatus = 'idle' | 'capturing' | 'analyzing' | 'complete' | 'error';
|
|
42
|
+
|
|
43
|
+
export type { ConfidenceTier as C, PhotoStatus as P, RiskTier as R, PhotoRegion as a, PhotoAnalysisResult as b, PhotoGateOutcome as c, PrecheckReasonCode as d };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics sink. `track` records an event; `trackExpectedFailure` records a handled, product-expected
|
|
3
|
+
* failure (a settled `EverfurError`, never a crash) so expected failures stay off the crash channel.
|
|
4
|
+
*/
|
|
5
|
+
interface TelemetryPort {
|
|
6
|
+
track(event: string, props?: Record<string, unknown>): void;
|
|
7
|
+
trackExpectedFailure(code: string, props?: Record<string, unknown>): void;
|
|
8
|
+
}
|
|
9
|
+
/** The safe default: a telemetry port that discards everything. Used when the host injects none. */
|
|
10
|
+
declare const noopTelemetry: TelemetryPort;
|
|
11
|
+
|
|
12
|
+
export { type TelemetryPort as T, noopTelemetry as n };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics sink. `track` records an event; `trackExpectedFailure` records a handled, product-expected
|
|
3
|
+
* failure (a settled `EverfurError`, never a crash) so expected failures stay off the crash channel.
|
|
4
|
+
*/
|
|
5
|
+
interface TelemetryPort {
|
|
6
|
+
track(event: string, props?: Record<string, unknown>): void;
|
|
7
|
+
trackExpectedFailure(code: string, props?: Record<string, unknown>): void;
|
|
8
|
+
}
|
|
9
|
+
/** The safe default: a telemetry port that discards everything. Used when the host injects none. */
|
|
10
|
+
declare const noopTelemetry: TelemetryPort;
|
|
11
|
+
|
|
12
|
+
export { type TelemetryPort as T, noopTelemetry as n };
|