@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,501 @@
|
|
|
1
|
+
import { E as EverfurResult, f as EverfurError } from './EverfurResult-D92-uL82.js';
|
|
2
|
+
|
|
3
|
+
/** The closed set of frame discriminators. `unknown` is the forward-compat catch-all. */
|
|
4
|
+
type FrameKind = 'accepted' | 'heartbeat' | 'delta' | 'done' | 'error' | 'analysisStarted' | 'analysisComplete' | 'unknown';
|
|
5
|
+
/** One of `emergency` | `schedule_soon` | `home_care`, or null when the slot is empty. */
|
|
6
|
+
type UrgencyLevel = 'emergency' | 'schedule_soon' | 'home_care';
|
|
7
|
+
interface FrameBase {
|
|
8
|
+
/** SSE `id:` line value, or null for pre-stream frames emitted before the sequence counter exists. */
|
|
9
|
+
readonly lastEventId: string | null;
|
|
10
|
+
/** A terminal frame ends the stream; frames after it are dropped. */
|
|
11
|
+
readonly terminal: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* The `X-Request-ID` of the HTTP response that opened this stream, hoisted onto EVERY frame so a
|
|
14
|
+
* failure discovered mid-stream still carries something a partner can quote. A stream has already
|
|
15
|
+
* returned 200 and its headers by the time an SSE error frame is written, so the frame body itself
|
|
16
|
+
* carries no request id (the contract records that gap explicitly); the header captured at open time
|
|
17
|
+
* is the only correlation handle there is.
|
|
18
|
+
*
|
|
19
|
+
* null when unavailable: the server sent no header, or the RN EventSource fallback path is in use
|
|
20
|
+
* (react-native-sse exposes no response headers to its consumer).
|
|
21
|
+
*
|
|
22
|
+
* DISTINCT from `AnalysisStartedFrame.requestId`, which is the analysis job id minted per analyze call
|
|
23
|
+
* (`chat_analysis_service.analyze_in_chat`) and travels in the frame payload, not the HTTP headers.
|
|
24
|
+
*/
|
|
25
|
+
readonly serverRequestId: string | null;
|
|
26
|
+
}
|
|
27
|
+
/** First frame of a send. Wire: `{type:'accepted'}` OR legacy `{status:'reasoning'}`. */
|
|
28
|
+
interface AcceptedFrame extends FrameBase {
|
|
29
|
+
readonly kind: 'accepted';
|
|
30
|
+
readonly streamId: string | null;
|
|
31
|
+
readonly terminal: false;
|
|
32
|
+
}
|
|
33
|
+
/** Keep-alive (~15s). Silent; resets client stall timers only. Wire: `{type:'heartbeat'}` OR `{heartbeat:true}`. */
|
|
34
|
+
interface HeartbeatFrame extends FrameBase {
|
|
35
|
+
readonly kind: 'heartbeat';
|
|
36
|
+
readonly terminal: false;
|
|
37
|
+
}
|
|
38
|
+
/** One streamed token. `delta` == legacy `token`. Wire: `{type:'text.delta'}` OR `{delta}`/`{token}`. */
|
|
39
|
+
interface DeltaFrame extends FrameBase {
|
|
40
|
+
readonly kind: 'delta';
|
|
41
|
+
readonly delta: string;
|
|
42
|
+
readonly terminal: false;
|
|
43
|
+
}
|
|
44
|
+
/** Terminal success. Wire: `{type:'done'}` OR `{done:true}` (bare-done is still terminal). */
|
|
45
|
+
interface DoneFrame extends FrameBase {
|
|
46
|
+
readonly kind: 'done';
|
|
47
|
+
readonly terminal: true;
|
|
48
|
+
/** '' if the wire omitted conversation_id. */
|
|
49
|
+
readonly conversationId: string;
|
|
50
|
+
readonly messageId: string | null;
|
|
51
|
+
/**
|
|
52
|
+
* PLANE-SPECIFIC, measured 2026-08-22. Populated on the CONSUMER chat plane, whose `_sse_done` writes it
|
|
53
|
+
* onto the done payload. **Always null on the `/widget/v1` partner plane this SDK targets**: that router
|
|
54
|
+
* builds a done payload of only `{done, conversation_id, follow_up_questions}` (+ optional message_id,
|
|
55
|
+
* citations, clinical_sections) and defers `title` / urgency to a LATER `{"metadata": true}` frame — which
|
|
56
|
+
* `done`'s terminal semantics stop this parser from reading.
|
|
57
|
+
*
|
|
58
|
+
* The parse is deliberately kept: it is correct on the consumer plane, the cross-language SSE conformance
|
|
59
|
+
* vectors pin it, and these are public fields a partner may already read via `handlers.onFrame`.
|
|
60
|
+
*
|
|
61
|
+
* Do NOT "fix" this by making `done` non-terminal. The server guards the metadata yield behind
|
|
62
|
+
* `if metadata:`, so a turn with no title/urgency ends right after `done`, and this parser's EOF path
|
|
63
|
+
* would then synthesize a terminal `streamIncomplete` ERROR on every such turn — converting a working
|
|
64
|
+
* turn into a user-visible failure. If the widget plane needs urgency, the field belongs on the server's
|
|
65
|
+
* done payload, at the cost of re-serializing the 1-5s metadata generation ahead of it.
|
|
66
|
+
*/
|
|
67
|
+
readonly title: string | null;
|
|
68
|
+
/**
|
|
69
|
+
* Present on the widget plane's done payload, but it carries `[]` there — the GENERATED follow-ups ride
|
|
70
|
+
* the later metadata frame (see `title`). Populated for real on the consumer plane.
|
|
71
|
+
*/
|
|
72
|
+
readonly followUpQuestions: readonly string[];
|
|
73
|
+
readonly citations: readonly Record<string, unknown>[];
|
|
74
|
+
readonly citationStatus: string | null;
|
|
75
|
+
/**
|
|
76
|
+
* null = slot present but empty; absent on non-urgency turns (folded to null here).
|
|
77
|
+
* Consumer plane only in practice — see `title` for why this is always null on `/widget/v1`.
|
|
78
|
+
*/
|
|
79
|
+
readonly urgencyLevel: UrgencyLevel | null;
|
|
80
|
+
/** Consumer plane only in practice — see `title`. */
|
|
81
|
+
readonly urgencyReason: string | null;
|
|
82
|
+
/** present on the terminal frame of an analyze stream. */
|
|
83
|
+
readonly resultId: string | null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Terminal error. `wireMessage` is UNTRUSTED and MUST NOT reach the UI (§3).
|
|
87
|
+
* Wire: `{type:'error'}` OR `{error}`. Normalize `code` via `fromWire` (./errors/codes.ts).
|
|
88
|
+
*/
|
|
89
|
+
interface ErrorFrame extends FrameBase {
|
|
90
|
+
readonly kind: 'error';
|
|
91
|
+
readonly terminal: true;
|
|
92
|
+
/** raw wire code, mixed casing; normalize via `fromWire`. */
|
|
93
|
+
readonly code: string;
|
|
94
|
+
/** wire `retryable === true`. */
|
|
95
|
+
readonly retryable: boolean;
|
|
96
|
+
/** raw `error` text; telemetry/logs only, NEVER rendered. */
|
|
97
|
+
readonly wireMessage: string | null;
|
|
98
|
+
/** true when the SDK fabricated this (body ended without a terminal frame). */
|
|
99
|
+
readonly synthesized: boolean;
|
|
100
|
+
}
|
|
101
|
+
/** First frame of POST /analyze (skin/eye/audio). Keyed on `event`. Wire: `{event:'analysis_started'}`. */
|
|
102
|
+
interface AnalysisStartedFrame extends FrameBase {
|
|
103
|
+
readonly kind: 'analysisStarted';
|
|
104
|
+
readonly requestId: string | null;
|
|
105
|
+
readonly terminal: false;
|
|
106
|
+
}
|
|
107
|
+
/** ML result before the AI discusses it. Wire: `{event:'analysis_complete'}`. */
|
|
108
|
+
interface AnalysisCompleteFrame extends FrameBase {
|
|
109
|
+
readonly kind: 'analysisComplete';
|
|
110
|
+
readonly terminal: false;
|
|
111
|
+
readonly resultId: string | null;
|
|
112
|
+
readonly analysisType: string | null;
|
|
113
|
+
readonly modelVersion: string | null;
|
|
114
|
+
readonly confidence: number | null;
|
|
115
|
+
readonly detections: readonly Record<string, unknown>[];
|
|
116
|
+
readonly interpretation: string | null;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Forward-compat catch-all. A discriminator this frozen build does not recognize degrades HERE; the
|
|
120
|
+
* parser NEVER throws on an unknown `type`/`event` (sse_frame.dart:127-130). A future backend frame
|
|
121
|
+
* type must not crash an app-store-frozen client. Non-terminal, so an unknown frame never ends a stream.
|
|
122
|
+
*/
|
|
123
|
+
interface UnknownFrame extends FrameBase {
|
|
124
|
+
readonly kind: 'unknown';
|
|
125
|
+
readonly raw: Record<string, unknown>;
|
|
126
|
+
readonly terminal: false;
|
|
127
|
+
}
|
|
128
|
+
/** The ONLY frame surface the rest of the SDK consumes. */
|
|
129
|
+
type Frame = AcceptedFrame | HeartbeatFrame | DeltaFrame | DoneFrame | ErrorFrame | AnalysisStartedFrame | AnalysisCompleteFrame | UnknownFrame;
|
|
130
|
+
/**
|
|
131
|
+
* Exhaustiveness guard for `switch (frame.kind)`. Adding a `FrameKind` fails the build at every switch
|
|
132
|
+
* that does not handle it. The single deliberate escape is `UnknownFrame`, which every switch handles
|
|
133
|
+
* explicitly; `assertNever` is reached only for a genuinely impossible (non-additive) variant.
|
|
134
|
+
*
|
|
135
|
+
* This lives in the framework-free client layer, where a plain `throw` is permitted (the
|
|
136
|
+
* `no-throw-nonconfig` rule scopes to src/core and src/rn only).
|
|
137
|
+
*/
|
|
138
|
+
declare function assertNever(x: never): never;
|
|
139
|
+
declare function acceptedFrame(streamId: string | null, lastEventId: string | null, serverRequestId?: string | null): AcceptedFrame;
|
|
140
|
+
declare function heartbeatFrame(lastEventId: string | null, serverRequestId?: string | null): HeartbeatFrame;
|
|
141
|
+
declare function deltaFrame(delta: string, lastEventId: string | null, serverRequestId?: string | null): DeltaFrame;
|
|
142
|
+
declare function doneFrame(fields: Omit<DoneFrame, 'kind' | 'terminal' | 'serverRequestId'>, serverRequestId?: string | null): DoneFrame;
|
|
143
|
+
declare function errorFrame(fields: {
|
|
144
|
+
code: string;
|
|
145
|
+
retryable: boolean;
|
|
146
|
+
wireMessage: string | null;
|
|
147
|
+
synthesized: boolean;
|
|
148
|
+
lastEventId: string | null;
|
|
149
|
+
serverRequestId?: string | null;
|
|
150
|
+
}): ErrorFrame;
|
|
151
|
+
declare function analysisStartedFrame(requestId: string | null, lastEventId: string | null, serverRequestId?: string | null): AnalysisStartedFrame;
|
|
152
|
+
declare function analysisCompleteFrame(fields: Omit<AnalysisCompleteFrame, 'kind' | 'terminal' | 'serverRequestId'>, serverRequestId?: string | null): AnalysisCompleteFrame;
|
|
153
|
+
declare function unknownFrame(raw: Record<string, unknown>, lastEventId: string | null, serverRequestId?: string | null): UnknownFrame;
|
|
154
|
+
declare const EMPTY_FRAME_ARRAYS: Readonly<{
|
|
155
|
+
strings: readonly string[];
|
|
156
|
+
maps: readonly Record<string, unknown>[];
|
|
157
|
+
}>;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The byte-source abstraction. Any transport tier (react-native-sse, expo/fetch,
|
|
161
|
+
* buffered text) adapts to this. Interchangeable so the parser stays pure.
|
|
162
|
+
*/
|
|
163
|
+
type ByteSource = AsyncIterable<Uint8Array>;
|
|
164
|
+
/**
|
|
165
|
+
* How the parser learns the stream's `X-Request-ID`.
|
|
166
|
+
*
|
|
167
|
+
* A plain `string | null` is the fetch path: the transport has already read the response headers before
|
|
168
|
+
* the first byte of the body, so the id is known up front. A THUNK is the RN EventSource path: the
|
|
169
|
+
* connection is opened by the adapter and its response headers only exist once the underlying XHR has
|
|
170
|
+
* reached HEADERS_RECEIVED, which happens after `parseSse` has been handed the byte source. The thunk is
|
|
171
|
+
* resolved lazily and memoized on its first non-null answer, so the id still lands on every frame the
|
|
172
|
+
* stream emits without the transport having to block on the open.
|
|
173
|
+
*/
|
|
174
|
+
type ServerRequestIdSource = string | null | (() => string | null);
|
|
175
|
+
/**
|
|
176
|
+
* Pure: bytes in, typed frames out. Never throws; a malformed block is skipped, not fatal.
|
|
177
|
+
*
|
|
178
|
+
* Behavioral contract (identical in TS and Dart, sse_parser.dart:82-118):
|
|
179
|
+
* 1. Decode UTF-8 incrementally; split on `\n\n`; buffer a partial trailing block across chunks.
|
|
180
|
+
* 2. Per block: read optional `id:` and one-or-more `data:` lines; ignore blank / `:`-comment lines;
|
|
181
|
+
* strip exactly one leading space after `data:`; join multi-line `data:` with `\n`; JSON.parse.
|
|
182
|
+
* 3. Dispatch is terminal-first (see frameFromWire).
|
|
183
|
+
* 4. Stop after the first terminal frame; later frames are dropped (not yielded).
|
|
184
|
+
* 5. Synthesize on incomplete: EOF with no terminal frame emits a terminal synthesized ErrorFrame.
|
|
185
|
+
* 6. A JSON parse error on one block skips that block only; the stream keeps flowing.
|
|
186
|
+
*
|
|
187
|
+
* `serverRequestId` is the `X-Request-ID` of the response that opened the stream. It is stamped onto every
|
|
188
|
+
* frame this parser emits, including the synthesized terminal errors below, because an SSE error frame
|
|
189
|
+
* carries no request id of its own: by the time the failure is known the 200 and its headers are already
|
|
190
|
+
* on the wire. On the fetch path the transport reads it BEFORE the body and passes the string; on the RN
|
|
191
|
+
* EventSource path it passes a thunk, because the adapter's response headers only become readable after
|
|
192
|
+
* the connection opens (see `ServerRequestIdSource`). Null when the adapter cannot expose one at all, which
|
|
193
|
+
* is honest: the parser never invents an id.
|
|
194
|
+
*/
|
|
195
|
+
declare function parseSse(bytes: ByteSource, serverRequestId?: ServerRequestIdSource): AsyncIterable<Frame>;
|
|
196
|
+
|
|
197
|
+
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
198
|
+
interface EverfurRequestBase {
|
|
199
|
+
readonly method: HttpMethod;
|
|
200
|
+
/** relative path, e.g. /widget/v1/entitlements; a leading slash is optional. */
|
|
201
|
+
readonly path: string;
|
|
202
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
203
|
+
readonly query?: Readonly<Record<string, string | number | undefined>>;
|
|
204
|
+
readonly signal?: AbortSignal;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* One request, in exactly one body shape.
|
|
208
|
+
*
|
|
209
|
+
* `body` and `multipart` are MUTUALLY EXCLUSIVE, and that is enforced by the type rather than left to a
|
|
210
|
+
* docblock. Carrying both is not a preference but a broken request: the JSON branch would set a
|
|
211
|
+
* `content-type` without the platform-generated multipart boundary, and the server cannot parse the result.
|
|
212
|
+
* The suppression that avoids it is a single `&&` clause at three call sites, exactly the kind of line a
|
|
213
|
+
* later edit deletes as redundant -- so the illegal combination is made unrepresentable instead.
|
|
214
|
+
*/
|
|
215
|
+
type EverfurRequest = EverfurRequestBase & ({
|
|
216
|
+
/**
|
|
217
|
+
* JSON-serialized on EVERY path: `requestJson`, `requestRaw`, and both `stream()` branches (the
|
|
218
|
+
* fetch branch via `buildInit`, the RN EventSource branch via `buildEventSourceInit`).
|
|
219
|
+
*
|
|
220
|
+
* This line previously read "ignored by requestRaw / stream", which is false and load-bearing:
|
|
221
|
+
* `ChatController.buildSendRequest` puts the user's message in `body` and sends it over `stream()`.
|
|
222
|
+
* Believing the docblock is what produced the multipart content-type finding.
|
|
223
|
+
*/
|
|
224
|
+
readonly body?: unknown;
|
|
225
|
+
readonly multipart?: undefined;
|
|
226
|
+
} | {
|
|
227
|
+
readonly body?: undefined;
|
|
228
|
+
/**
|
|
229
|
+
* A multipart body, passed to `fetch` UNTOUCHED so the platform sets the `multipart/form-data`
|
|
230
|
+
* boundary itself. Nothing is JSON-serialized and no `content-type` header is set.
|
|
231
|
+
*
|
|
232
|
+
* Exists for `POST /widget/v1/conversations/{id}/analyze`, the partner plane's photo/audio analysis
|
|
233
|
+
* route, which takes a file. Every other call on this transport stays JSON.
|
|
234
|
+
*/
|
|
235
|
+
readonly multipart: MultipartBody;
|
|
236
|
+
});
|
|
237
|
+
interface RawResponse {
|
|
238
|
+
readonly ok: boolean;
|
|
239
|
+
readonly status: number;
|
|
240
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
241
|
+
readonly bytes: ByteSource;
|
|
242
|
+
}
|
|
243
|
+
/** The port both the real transport and the MockTransport implement. Exactly two wire shapes. */
|
|
244
|
+
interface TransportPort {
|
|
245
|
+
request<T>(req: EverfurRequest): Promise<EverfurResult<T>>;
|
|
246
|
+
stream(req: EverfurRequest, signal: AbortSignal): AsyncIterable<Frame>;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* The FormData surface this layer needs. Declared structurally, like `FetchInit` and `FetchResponseLike`
|
|
250
|
+
* above, so the framework-free client depends on no DOM lib and no react-native import. A platform
|
|
251
|
+
* `FormData` satisfies it.
|
|
252
|
+
*/
|
|
253
|
+
interface MultipartBody {
|
|
254
|
+
append(name: string, value: unknown, fileName?: string): void;
|
|
255
|
+
}
|
|
256
|
+
interface FetchInit {
|
|
257
|
+
method: string;
|
|
258
|
+
headers: Record<string, string>;
|
|
259
|
+
body?: string | MultipartBody;
|
|
260
|
+
signal?: AbortSignal;
|
|
261
|
+
}
|
|
262
|
+
interface ReadableStreamLike {
|
|
263
|
+
getReader(): {
|
|
264
|
+
read(): Promise<{
|
|
265
|
+
done: boolean;
|
|
266
|
+
value?: Uint8Array;
|
|
267
|
+
}>;
|
|
268
|
+
cancel?(): void | Promise<void>;
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
interface FetchResponseLike {
|
|
272
|
+
readonly ok: boolean;
|
|
273
|
+
readonly status: number;
|
|
274
|
+
readonly headers: {
|
|
275
|
+
get(name: string): string | null;
|
|
276
|
+
forEach?(cb: (v: string, k: string) => void): void;
|
|
277
|
+
};
|
|
278
|
+
readonly body: ReadableStreamLike | null;
|
|
279
|
+
text(): Promise<string>;
|
|
280
|
+
json(): Promise<unknown>;
|
|
281
|
+
}
|
|
282
|
+
type FetchLike = (input: string, init: FetchInit) => Promise<FetchResponseLike>;
|
|
283
|
+
interface TransportDeps {
|
|
284
|
+
readonly config: ResolvedClientConfig;
|
|
285
|
+
/** default globalThis.fetch; injected for tests. */
|
|
286
|
+
readonly fetchImpl?: FetchLike;
|
|
287
|
+
/**
|
|
288
|
+
* Injected EventSource factory (react-native-sse on RN, wired from the rn layer). Used ONLY when a streaming
|
|
289
|
+
* `res.body` is unavailable (stock RN fetch, >=0.74): the SSE turn streams over XHR instead of silently
|
|
290
|
+
* failing to streamIncomplete. Absent, or on web/expo where `res.body` streams, the fetch byte-source path is
|
|
291
|
+
* used unchanged. Kept as an injected seam so Layer 1 never imports react-native-sse (native-free client).
|
|
292
|
+
*/
|
|
293
|
+
readonly eventSourceFactory?: EventSourceFactory;
|
|
294
|
+
}
|
|
295
|
+
/** expo / fetch: read a Response body (a ReadableStream of Uint8Array). */
|
|
296
|
+
declare function fetchByteSource(body: ReadableStreamLike | null): ByteSource;
|
|
297
|
+
/** buffered text: yield a whole SSE body, optionally split at byte offsets (drives the chunk-split vector). */
|
|
298
|
+
declare function bufferedTextByteSource(text: string, splitAt?: readonly number[]): ByteSource;
|
|
299
|
+
/** A minimal react-native-sse EventSource surface (parsed events), injected to keep Layer 1 native-free. */
|
|
300
|
+
interface EventSourceMessage {
|
|
301
|
+
readonly lastEventId?: string | null;
|
|
302
|
+
readonly data?: string | null;
|
|
303
|
+
/**
|
|
304
|
+
* On an `error` event (react-native-sse ErrorEvent): the underlying XHR HTTP status. A non-2xx status here is
|
|
305
|
+
* a pre-stream HTTP error (401/403/429/...) that must map to the SAME terminal wire code the fetch path
|
|
306
|
+
* produces; 0 / null / undefined is a transport-level failure (no HTTP response) -> a retryable synthetic error.
|
|
307
|
+
*/
|
|
308
|
+
readonly xhrStatus?: number | null;
|
|
309
|
+
}
|
|
310
|
+
interface EventSourceLike {
|
|
311
|
+
addEventListener(type: string, listener: (ev: EventSourceMessage) => void): void;
|
|
312
|
+
removeEventListener(type: string, listener: (ev: EventSourceMessage) => void): void;
|
|
313
|
+
close(): void;
|
|
314
|
+
/**
|
|
315
|
+
* A RESPONSE header of the connection the adapter opened, or null when it is not (yet) readable.
|
|
316
|
+
*
|
|
317
|
+
* OPTIONAL, because it is an adapter capability rather than part of the EventSource idea: react-native-sse
|
|
318
|
+
* hands its consumer parsed events only, so the rn adapter reaches the header off the XMLHttpRequest it
|
|
319
|
+
* holds. An adapter that cannot expose one simply omits this method, and the stream's frames carry a null
|
|
320
|
+
* `serverRequestId` rather than an invented value.
|
|
321
|
+
*
|
|
322
|
+
* Read LAZILY. An XHR only exposes response headers from HEADERS_RECEIVED onward, which is after the
|
|
323
|
+
* factory returned, so the transport passes a thunk to `parseSse` instead of a value.
|
|
324
|
+
*/
|
|
325
|
+
getResponseHeader?(name: string): string | null;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Init for an injected EventSource open, a structural subset of react-native-sse's EventSourceOptions. Layer 1
|
|
329
|
+
* stays native-free: it names only the fields streamFrames sets, never the concrete react-native-sse type.
|
|
330
|
+
*/
|
|
331
|
+
interface EventSourceInit {
|
|
332
|
+
readonly method: string;
|
|
333
|
+
readonly headers: Record<string, string>;
|
|
334
|
+
/** JSON string; present for a POST send, absent for a bodyless stream. */
|
|
335
|
+
readonly body?: string;
|
|
336
|
+
/** Disable react-native-sse's auto-reconnect: a chat turn is a single one-shot stream, not a polling loop. */
|
|
337
|
+
readonly pollingInterval?: number;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Opens an EventSource for a URL. INJECTED (never imported) into Layer 1 so the client stays native-free: the
|
|
341
|
+
* concrete factory is react-native-sse in the rn layer, a fake in tests. Used only on the RN fallback path.
|
|
342
|
+
*/
|
|
343
|
+
type EventSourceFactory = (url: string, init: EventSourceInit) => EventSourceLike;
|
|
344
|
+
/**
|
|
345
|
+
* react-native-sse: reconstruct raw SSE wire bytes from parsed EventSource events so parseSse stays the ONE
|
|
346
|
+
* frame authority (a library that pre-parses SSE is re-serialized back to `id:` / `data:` blocks here).
|
|
347
|
+
*/
|
|
348
|
+
declare function eventSourceByteSource(es: EventSourceLike): ByteSource;
|
|
349
|
+
/**
|
|
350
|
+
* Unary JSON request. Never throws for an expected 4xx/5xx; maps it to a typed EverfurError.
|
|
351
|
+
*
|
|
352
|
+
* Every unary hop is bounded by a per-request deadline (PERF2): a stalled connection settles to a retryable
|
|
353
|
+
* `internalError` instead of hanging the caller's spinner forever. The deadline is MERGED with any caller
|
|
354
|
+
* `req.signal` (each individual attempt is bounded even under a long-lived caller signal, e.g. the poll loop),
|
|
355
|
+
* and the timer is always cleared once the fetch settles so a completed request leaves no dangling timer.
|
|
356
|
+
*/
|
|
357
|
+
declare function requestJson<T>(req: EverfurRequest, deps: TransportDeps): Promise<EverfurResult<T>>;
|
|
358
|
+
/** Raw request for streaming. On a non-2xx it settles to an Err; on 2xx it hands back a ByteSource. */
|
|
359
|
+
declare function requestRaw(req: EverfurRequest, deps: TransportDeps, byteSourceFor?: (res: FetchResponseLike) => ByteSource): Promise<EverfurResult<RawResponse>>;
|
|
360
|
+
declare function createHttpTransport(deps: TransportDeps): TransportPort;
|
|
361
|
+
/**
|
|
362
|
+
* Build a typed EverfurError from a response status + parsed body. Prefers the wire code over the status.
|
|
363
|
+
*
|
|
364
|
+
* `requestId` is the captured `X-Request-ID`. The raw wire code is handed through so the contract
|
|
365
|
+
* registry can resolve the row and, from it, the site-relative `docUrl`; the parsed body rides along as
|
|
366
|
+
* the non-enumerable `raw` for debugging. `retryable` is only FORCED when the body says so explicitly,
|
|
367
|
+
* otherwise it is derived from the code, which is how a registry-declared retryable code (a 503
|
|
368
|
+
* `unavailable`, say) gets its retry affordance without the caller restating the policy.
|
|
369
|
+
*/
|
|
370
|
+
declare function errorFromResponse(status: number, body: unknown, requestId?: string | null): EverfurError;
|
|
371
|
+
/** A fetch rejection (offline / DNS): retryable internalError; the cause text is telemetry-only. */
|
|
372
|
+
declare function networkError(cause: unknown): EverfurError;
|
|
373
|
+
/**
|
|
374
|
+
* The `X-Request-ID` of a response, or null. Read through `headers.get`, which is case-insensitive on a
|
|
375
|
+
* real `Headers`; an injected fetch whose header bag is a plain object is expected to match the same
|
|
376
|
+
* lowercase spelling the rest of this file uses.
|
|
377
|
+
*/
|
|
378
|
+
/**
|
|
379
|
+
* The `X-Request-ID` of a completed response, or null.
|
|
380
|
+
*
|
|
381
|
+
* Exported so the server-side session mint reuses this one implementation. The mint runs on the
|
|
382
|
+
* partner's backend and deliberately does NOT go through the device funnel (that funnel enforces the
|
|
383
|
+
* bearer-XOR-publishable-key device rule, which has no meaning server-side), but a failed mint still
|
|
384
|
+
* needs a request id a partner can quote in a support ticket.
|
|
385
|
+
*/
|
|
386
|
+
declare function requestIdOf(res: FetchResponseLike): string | null;
|
|
387
|
+
/**
|
|
388
|
+
* The `X-Request-ID` of an open EventSource connection, or null.
|
|
389
|
+
*
|
|
390
|
+
* The EventSource counterpart of `requestIdOf`. Null in three honest cases, none of them an error: the
|
|
391
|
+
* adapter exposes no header accessor at all, the response headers have not landed yet, or the server sent
|
|
392
|
+
* no id. Defensive around the accessor because it reaches into a native XHR: a throwing adapter must
|
|
393
|
+
* degrade to "no id", never take the stream down.
|
|
394
|
+
*/
|
|
395
|
+
declare function eventSourceRequestId(es: EventSourceLike): string | null;
|
|
396
|
+
declare function getGlobalFetch(): FetchLike;
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Package semver, sent as X-Everfur-SDK-Version. MUST track package.json version (release checklist Item 12
|
|
400
|
+
* keeps the npm semver tag and this constant in lockstep). Set inside the SDK, never by partner code.
|
|
401
|
+
*/
|
|
402
|
+
declare const SDK_VERSION = "0.1.0";
|
|
403
|
+
/** Sent as X-Everfur-SDK-Platform. */
|
|
404
|
+
declare const SDK_PLATFORM = "react-native";
|
|
405
|
+
/** Dated wire contract axis (SPEC-00 §4.2). Server is additive-only. Override via config.contractVersion. */
|
|
406
|
+
declare const DEFAULT_CONTRACT_VERSION = "2026-08-01";
|
|
407
|
+
/** The environment variable the base URL is derived from when config.apiBaseUrl is absent. */
|
|
408
|
+
declare const ENV_API_BASE_URL = "EVERFUR_API_BASE_URL";
|
|
409
|
+
interface EverfurConfig {
|
|
410
|
+
/** pk_live_... ; tenant-resolved server-side. The keyless-fallback (anonymous-mode) credential. */
|
|
411
|
+
readonly publishableKey: string;
|
|
412
|
+
/** REQUIRED via config or EVERFUR_API_BASE_URL; no hardcoded host default (INV-ORG-AGNOSTIC). */
|
|
413
|
+
readonly apiBaseUrl?: string;
|
|
414
|
+
/** Sent as X-Everfur-Contract; server additive-only. Defaults to DEFAULT_CONTRACT_VERSION. */
|
|
415
|
+
readonly contractVersion?: string;
|
|
416
|
+
/** Test seam; production builds the real transport from apiBaseUrl. */
|
|
417
|
+
readonly transport?: TransportPort;
|
|
418
|
+
/** Structured, redacted LogPort events when true. */
|
|
419
|
+
readonly debug?: boolean;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* The validated, frozen subset the Layer 1 transport / identity actually consume.
|
|
423
|
+
*/
|
|
424
|
+
interface ResolvedClientConfig {
|
|
425
|
+
readonly publishableKey: string;
|
|
426
|
+
/** normalized absolute URL, no trailing slash */
|
|
427
|
+
readonly apiBaseUrl: string;
|
|
428
|
+
readonly contractVersion: string;
|
|
429
|
+
readonly sdkVersion: string;
|
|
430
|
+
readonly platform: string;
|
|
431
|
+
readonly debug: boolean;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* CONFIG FAILURE IS A VALUE, NOT A THROW.
|
|
435
|
+
*
|
|
436
|
+
* Every check has exactly one implementation, in `tryResolveClientConfig`, which SETTLES to an outcome.
|
|
437
|
+
* `resolveClientConfig` is a thin throwing wrapper over it and preserves the historical messages
|
|
438
|
+
* verbatim, so a caller that wants fail-fast keeps it, while `EverfurProvider` can instead build a
|
|
439
|
+
* DISABLED runtime and still render children. SPEC-10 §8 already establishes non-throwing init: see
|
|
440
|
+
* `core/bootstrap.ts`, "A failure is a VALUE, not a throw".
|
|
441
|
+
*
|
|
442
|
+
* Why this matters in a RELEASE build: `babel-preset-expo` inlines only `EXPO_PUBLIC_*`, so a partner
|
|
443
|
+
* who sets a correctly-named but unprefixed variable ships a bundle where `EVERFUR_API_BASE_URL` is
|
|
444
|
+
* absent at runtime. Every local check passed. The throw then lands inside the provider's own render,
|
|
445
|
+
* ABOVE every error boundary the partner can install, which is a white screen with no diagnostic.
|
|
446
|
+
*/
|
|
447
|
+
type ClientConfigFailureReason = 'missing-publishable-key' | 'secret-key-as-publishable-key' | 'missing-api-base-url' | 'malformed-api-base-url' | 'insecure-api-base-url' | 'unsupported-api-base-url-scheme';
|
|
448
|
+
interface ClientConfigFailure {
|
|
449
|
+
readonly reason: ClientConfigFailureReason;
|
|
450
|
+
/**
|
|
451
|
+
* Developer-facing copy. Names the field and how to supply it, and NEVER carries a credential.
|
|
452
|
+
*
|
|
453
|
+
* This is load-bearing, not decorative: `reportConfigFailure` prints this string to `console.error` on
|
|
454
|
+
* every mount, and on React Native `console.error` is captured as breadcrumbs by Sentry / Crashlytics /
|
|
455
|
+
* Bugsnag and written to logcat and the Xcode device log. So the URL branches echo a REDACTED ORIGIN
|
|
456
|
+
* (`scheme://host`) built by `safeOrigin`, never the raw value: a raw base URL can carry userinfo
|
|
457
|
+
* (`http://svc:s3cr3t@internal.example.com`), and cleartext-with-basic-auth is exactly the shape that
|
|
458
|
+
* lands in the insecure branch. The malformed branch echoes nothing at all, because an unparseable value
|
|
459
|
+
* is most often a mis-paste of something else, such as a key.
|
|
460
|
+
*/
|
|
461
|
+
readonly message: string;
|
|
462
|
+
}
|
|
463
|
+
type ClientConfigOutcome = {
|
|
464
|
+
readonly ok: true;
|
|
465
|
+
readonly config: ResolvedClientConfig;
|
|
466
|
+
} | {
|
|
467
|
+
readonly ok: false;
|
|
468
|
+
readonly failure: ClientConfigFailure;
|
|
469
|
+
};
|
|
470
|
+
type BaseUrlOutcome = {
|
|
471
|
+
readonly ok: true;
|
|
472
|
+
readonly value: string;
|
|
473
|
+
} | {
|
|
474
|
+
readonly ok: false;
|
|
475
|
+
readonly failure: ClientConfigFailure;
|
|
476
|
+
};
|
|
477
|
+
/**
|
|
478
|
+
* The ONE implementation of the publishable-key rule. Exported because the runtime validates the key even
|
|
479
|
+
* on the injected-transport path, where the base URL is not required and `tryResolveClientConfig` is
|
|
480
|
+
* therefore never called; without this both places would carry a copy and drift.
|
|
481
|
+
*/
|
|
482
|
+
declare function checkPublishableKey(raw: unknown): ClientConfigFailure | null;
|
|
483
|
+
/** Validate the partner-supplied config into the frozen client subset. Settles; never throws. */
|
|
484
|
+
declare function tryResolveClientConfig(config: EverfurConfig): ClientConfigOutcome;
|
|
485
|
+
/**
|
|
486
|
+
* Validate the partner-supplied config into the frozen client subset. Throws EverfurConfigError
|
|
487
|
+
* synchronously for programmer error (missing publishableKey, unresolvable/invalid base URL) -- never for
|
|
488
|
+
* network/domain failure. Prefer `tryResolveClientConfig` wherever a blank screen is the alternative.
|
|
489
|
+
*/
|
|
490
|
+
declare function resolveClientConfig(config: EverfurConfig): ResolvedClientConfig;
|
|
491
|
+
/**
|
|
492
|
+
* Resolve the base URL from (in order) an explicit value then EVERFUR_API_BASE_URL, then normalize.
|
|
493
|
+
* Settles; never throws. There is deliberately no hardcoded host default (INV-ORG-AGNOSTIC).
|
|
494
|
+
*/
|
|
495
|
+
declare function tryResolveApiBaseUrl(apiBaseUrl?: string): BaseUrlOutcome;
|
|
496
|
+
/** Resolve the base URL, throwing on failure. Thin wrapper over `tryResolveApiBaseUrl`. */
|
|
497
|
+
declare function resolveApiBaseUrl(apiBaseUrl?: string): string;
|
|
498
|
+
/** The always-on SDK protocol headers (version / platform / contract). Set inside the transport only. */
|
|
499
|
+
declare function buildBaseHeaders(cfg: Pick<ResolvedClientConfig, 'sdkVersion' | 'platform' | 'contractVersion'>): Record<string, string>;
|
|
500
|
+
|
|
501
|
+
export { fetchByteSource as $, type AcceptedFrame as A, type ByteSource as B, type ClientConfigFailure as C, DEFAULT_CONTRACT_VERSION as D, type EverfurRequest as E, type Frame as F, type UrgencyLevel as G, type HeartbeatFrame as H, acceptedFrame as I, analysisCompleteFrame as J, analysisStartedFrame as K, assertNever as L, type MultipartBody as M, bufferedTextByteSource as N, buildBaseHeaders as O, checkPublishableKey as P, createHttpTransport as Q, type RawResponse as R, SDK_PLATFORM as S, type TransportPort as T, type UnknownFrame as U, deltaFrame as V, doneFrame as W, errorFrame as X, errorFromResponse as Y, eventSourceByteSource as Z, eventSourceRequestId as _, type FetchLike as a, getGlobalFetch as a0, heartbeatFrame as a1, networkError as a2, parseSse as a3, requestIdOf as a4, requestJson as a5, requestRaw as a6, resolveApiBaseUrl as a7, resolveClientConfig as a8, tryResolveApiBaseUrl as a9, tryResolveClientConfig as aa, unknownFrame as ab, type AnalysisCompleteFrame as b, type AnalysisStartedFrame as c, type ClientConfigFailureReason as d, type ClientConfigOutcome as e, type DeltaFrame as f, type DoneFrame as g, EMPTY_FRAME_ARRAYS as h, ENV_API_BASE_URL as i, type ErrorFrame as j, type EventSourceFactory as k, type EventSourceInit as l, type EventSourceLike as m, type EventSourceMessage as n, type EverfurConfig as o, type EverfurRequestBase as p, type FetchInit as q, type FetchResponseLike as r, type FrameBase as s, type FrameKind as t, type HttpMethod as u, type ReadableStreamLike as v, type ResolvedClientConfig as w, SDK_VERSION as x, type ServerRequestIdSource as y, type TransportDeps as z };
|