@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.
Files changed (89) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/LICENSE +21 -0
  3. package/README.md +161 -0
  4. package/animations/package.json +8 -0
  5. package/chat/package.json +8 -0
  6. package/client/package.json +8 -0
  7. package/core/package.json +8 -0
  8. package/dist/CameraPort-Cv31Pz7g.d.cts +29 -0
  9. package/dist/CameraPort-Cv31Pz7g.d.ts +29 -0
  10. package/dist/ChatController-CKdBvPj2.d.ts +146 -0
  11. package/dist/ChatController-CpUMvvZf.d.cts +146 -0
  12. package/dist/EverfurResult-D92-uL82.d.cts +240 -0
  13. package/dist/EverfurResult-D92-uL82.d.ts +240 -0
  14. package/dist/FilePort-BabWrv7I.d.cts +22 -0
  15. package/dist/FilePort-BabWrv7I.d.ts +22 -0
  16. package/dist/PhotoController-BItt5M7u.d.cts +43 -0
  17. package/dist/PhotoController-D8zMTdcW.d.ts +43 -0
  18. package/dist/TelemetryPort-BDNr00hu.d.cts +12 -0
  19. package/dist/TelemetryPort-BDNr00hu.d.ts +12 -0
  20. package/dist/animations/index.cjs +1997 -0
  21. package/dist/animations/index.d.cts +517 -0
  22. package/dist/animations/index.d.ts +517 -0
  23. package/dist/animations/index.js +1972 -0
  24. package/dist/cacheEpoch-DknKn3S0.d.cts +36 -0
  25. package/dist/cacheEpoch-DknKn3S0.d.ts +36 -0
  26. package/dist/chat/index.cjs +1170 -0
  27. package/dist/chat/index.d.cts +59 -0
  28. package/dist/chat/index.d.ts +59 -0
  29. package/dist/chat/index.js +1167 -0
  30. package/dist/client/index.cjs +2317 -0
  31. package/dist/client/index.d.cts +65 -0
  32. package/dist/client/index.d.ts +65 -0
  33. package/dist/client/index.js +2217 -0
  34. package/dist/config-BSjBdxrZ.d.cts +501 -0
  35. package/dist/config-CiJ0PVBB.d.ts +501 -0
  36. package/dist/core/index.cjs +3175 -0
  37. package/dist/core/index.d.cts +70 -0
  38. package/dist/core/index.d.ts +70 -0
  39. package/dist/core/index.js +3163 -0
  40. package/dist/identity-Brl-lDd6.d.cts +91 -0
  41. package/dist/identity-DK9zORrG.d.ts +91 -0
  42. package/dist/ids-CJ1S6adf.d.cts +46 -0
  43. package/dist/ids-CJ1S6adf.d.ts +46 -0
  44. package/dist/index.cjs +4488 -0
  45. package/dist/index.d.cts +156 -0
  46. package/dist/index.d.ts +156 -0
  47. package/dist/index.js +4464 -0
  48. package/dist/petsRepository-BEGb97M9.d.cts +326 -0
  49. package/dist/petsRepository-Bu18r2kK.d.ts +326 -0
  50. package/dist/photo/index.cjs +2189 -0
  51. package/dist/photo/index.d.cts +43 -0
  52. package/dist/photo/index.d.ts +43 -0
  53. package/dist/photo/index.js +2186 -0
  54. package/dist/projection-CeIUsbSk.d.cts +8 -0
  55. package/dist/projection-CeIUsbSk.d.ts +8 -0
  56. package/dist/records/index.cjs +2840 -0
  57. package/dist/records/index.d.cts +224 -0
  58. package/dist/records/index.d.ts +224 -0
  59. package/dist/records/index.js +2834 -0
  60. package/dist/requestFunnel-DuUH-kAe.d.cts +28 -0
  61. package/dist/requestFunnel-dio5OmR9.d.ts +28 -0
  62. package/dist/resolve-Dq_4_agU.d.cts +86 -0
  63. package/dist/resolve-Dq_4_agU.d.ts +86 -0
  64. package/dist/runtime-BgQnA594.d.cts +349 -0
  65. package/dist/runtime-CBA-LvdM.d.ts +349 -0
  66. package/dist/server/index.cjs +533 -0
  67. package/dist/server/index.d.cts +48 -0
  68. package/dist/server/index.d.ts +48 -0
  69. package/dist/server/index.js +530 -0
  70. package/dist/testing/index.cjs +825 -0
  71. package/dist/testing/index.d.cts +113 -0
  72. package/dist/testing/index.d.ts +113 -0
  73. package/dist/testing/index.js +822 -0
  74. package/dist/testing/rn/index.cjs +449 -0
  75. package/dist/testing/rn/index.d.cts +49 -0
  76. package/dist/testing/rn/index.d.ts +49 -0
  77. package/dist/testing/rn/index.js +444 -0
  78. package/dist/video/index.cjs +1941 -0
  79. package/dist/video/index.d.cts +66 -0
  80. package/dist/video/index.d.ts +66 -0
  81. package/dist/video/index.js +1938 -0
  82. package/package.json +311 -0
  83. package/photo/package.json +8 -0
  84. package/records/package.json +8 -0
  85. package/server/device-blocked.cjs +15 -0
  86. package/server/package.json +9 -0
  87. package/testing/package.json +8 -0
  88. package/testing/rn/package.json +8 -0
  89. package/video/package.json +8 -0
@@ -0,0 +1,501 @@
1
+ import { E as EverfurResult, f as EverfurError } from './EverfurResult-D92-uL82.cjs';
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 };