@fixback/expo 0.9.0 → 0.9.1

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/dist/client.d.ts CHANGED
@@ -12,268 +12,25 @@
12
12
  * `resolveIdentity`, then `armCapture` — rather than one long procedure, so the
13
13
  * lifecycle reads as what it is and each step is testable on its own.
14
14
  *
15
+ * The client is a few pieces over one shared state object (`./client-state`, #661):
16
+ * boot and the capture it arms (`./client-boot`), mobile replay's arming
17
+ * (`./client-replay`), and Connect (`./client-connect`). What stays here is the
18
+ * lifecycle (`start`, `destroy`) and the composer-facing surface (`present`,
19
+ * `submit`, `trackScreen`). The public shapes live in `./options` and are
20
+ * re-exported from here.
21
+ *
15
22
  * Every platform touchpoint is an injected adapter (`FixbackAdapters`), so the
16
23
  * whole orchestration is unit-tested without React Native. Every path is
17
24
  * defensive: a Fixback problem leaves the host app untouched — `start` resolves
18
25
  * to a `disabled` client instead of throwing.
19
26
  */
20
- import { type BootAnswer, type CaptureToggles, type CommonCaptureInitOptions, DEFAULT_API_URL, type FetchLike, type IdentityInputs, type MaintenanceNotice, type ReporterIdentity } from "@fixback/sdk-core";
21
- import { type ConsoleLike, type XhrConstructor } from "./breadcrumbs";
22
- import { type DismissBrowser, type LinkingLike, type OpenAuthSession, type OpenBrowser } from "./connect";
23
- import { type SecureStorageLike } from "./reporter-session-store";
24
- import { type AppStateLike, type ErrorUtilsLike } from "./error-capture";
25
- import type { FormDataFactory } from "./http";
26
- import { type StorageLike } from "./identity";
27
+ import { DEFAULT_API_URL } from "@fixback/sdk-core";
28
+ import type { FixbackAdapters, FixbackClient, FixbackOptions } from "./options";
27
29
  import { canonicalizeOrigin } from "./origin";
28
- import { type EnvironmentInputs } from "./report";
29
- import { type MobileReplayOptions } from "./replay";
30
- import { type ReplayEncodeDeps } from "./replay-encode";
31
- import { type ResolvedShakeConfig, type ShakeOptions } from "./shake";
32
- import { type ScreenshotFile, type SubmitResult } from "./submit";
33
30
  export { DEFAULT_API_URL };
34
31
  export { canonicalizeOrigin };
35
32
  export { DEFAULT_SHAKE_SAMPLE_INTERVAL_MS, type ResolvedShakeConfig, type ShakeOptions, } from "./shake";
36
33
  export { DEFAULT_REPLAY_FPS, DEFAULT_REPLAY_MAX_EDGE, type MobileReplayOptions, REPLAY_BUCKET_MS, } from "./replay";
37
- /**
38
- * Everything `FixbackProvider` / `createFixbackClient` accepts: the shared init
39
- * vocabulary (`apiUrl`, `release`, `enabled`, `scrub`, `beforeSend`,
40
- * `beforeBreadcrumb`, `capture`) plus what only a React Native app has. There is no
41
- * `environment`: the `key` belongs to one Environment, and the server resolves the
42
- * Environment and its Gate from it (ADR 2026-09-24).
43
- */
44
- export interface FixbackOptions extends CommonCaptureInitOptions, IdentityInputs {
45
- /** The Project's publishable key. Required. */
46
- readonly key: string;
47
- /**
48
- * The origin this app reports as, sent as the `Origin` header on every request
49
- * (spec 0004 §A). **Optional** since ADR-0041: a native app attaches no `Origin`
50
- * of its own, and ingest no longer demands one — a value the client invents proves
51
- * nothing about who is calling, so requiring it only cost a setup step. Omit it and
52
- * the app reports with no origin at all.
53
- *
54
- * Set one when you *want* mobile reports to carry an origin — it names the report's
55
- * `url` as `<origin>/<screen>` and gives the queue something to filter by. Then it
56
- * must be an `https://` origin allowlisted on the Project (convention: the app's
57
- * reverse-DNS id as host, `https://com.acme.myapp`, or the product's web origin),
58
- * because an origin that *is* presented is still checked.
59
- */
60
- readonly origin?: string;
61
- /**
62
- * Where the platform's connect page returns after a Connect **this app starts** —
63
- * a scheme (`myapp://fixback-connect`) or universal link the app handles, declared
64
- * once here and passed to the in-app auth session (ADR-0066; ticket #251). Its
65
- * origin must be allowlisted on the Project (the server validates the return URL).
66
- * Without it, `signIn` stays a no-op — a report still files anonymously where the
67
- * Gate allows.
68
- *
69
- * It is **not** needed for an **invite**: that Connect is minted against the app
70
- * link the Project allowlists and arrives as a deep link, which the SDK redeems
71
- * whether or not this option is set (ADR-0046). Set it when you want to offer
72
- * sign-in from inside the app; when it is set it also narrows which inbound links
73
- * the SDK will read a code off.
74
- */
75
- readonly returnUrl?: string;
76
- /**
77
- * Automatic error capture — the SDK files uncaught JS errors as `source: error`
78
- * Feedback with no prompt (spec 0004 §E, ADR-0011). Defaults to `true`; set
79
- * `false` for the per-project toggle that turns it off. It is gated by boot's
80
- * `canSubmit` either way, so an auto-error is never filed where a manual report
81
- * would be refused.
82
- */
83
- readonly autoCapture?: boolean;
84
- /**
85
- * Per-stream capture toggles; unset streams follow the boot answer — **except
86
- * `replay`**, which is off on mobile until this app asks for it (ADR-0044). A mobile
87
- * replay is an unmasked recording of the device screen, so updating the SDK must
88
- * never start one: set `capture: { replay: true }` (or pass a {@link replay} tuning
89
- * object) to opt in, and the Project's own replay toggle still has the final say.
90
- */
91
- readonly capture?: CaptureToggles;
92
- /**
93
- * Mobile session-replay tuning (ADR-0044) — cadence, encoded size, frame quality.
94
- * Passing it **also opts in**, exactly as `capture: { replay: true }` does; set
95
- * `capture: { replay: false }` to tune without recording.
96
- *
97
- * The window is a buffered 30–60 s of device frames at 1 fps, encoded to one MP4 at
98
- * report time. It is **not masked**: everything the app drew is in it, which is why
99
- * it is off by default and why the Project must enable replay too.
100
- */
101
- readonly replay?: MobileReplayOptions;
102
- /** Capture a screenshot when the composer opens. Defaults to `true`. */
103
- readonly screenshots?: boolean;
104
- /** Shake gesture tuning / off switch. */
105
- readonly shake?: ShakeOptions;
106
- /**
107
- * The origins of **your own APIs** that should receive the **Correlation id** — a
108
- * list of origins (`scheme://host[:port]`, e.g. `["https://api.example.com"]`).
109
- *
110
- * For a request to a listed origin the SDK sends a freshly minted W3C `traceparent`
111
- * header and records its trace id on the request's network entry; `@fixback/node` on
112
- * that backend adopts the same id for any error the request causes, so the app's
113
- * Issue and the server Issue show one id. An app has no "same origin" to default to,
114
- * so **nothing is sent unless its origin is listed** — a third-party API never sees a
115
- * header it did not ask for. No wildcards and no paths: an entry that is not a bare
116
- * origin is ignored with a warning. `fetch` is covered too — on React Native it runs
117
- * on `XMLHttpRequest`, which is what the SDK instruments — and nothing is sent while
118
- * network capture is off.
119
- */
120
- readonly propagateTo?: readonly string[];
121
- }
122
- /**
123
- * The platform touchpoints, as injectable seams. `createNativeAdapters()`
124
- * builds the real React Native set; tests pass fakes.
125
- */
126
- export interface FixbackAdapters {
127
- /** Anonymous-id persistence (AsyncStorage). Absent ⇒ ephemeral ids. */
128
- readonly storage?: StorageLike | null;
129
- /**
130
- * Secure persistence for the Reporter session token after a Connect (SecureStore).
131
- * Absent ⇒ no persisted session (Connect still works for this launch).
132
- */
133
- readonly secureStore?: SecureStorageLike | null;
134
- /**
135
- * Opens the platform's connect page in an in-app auth session and resolves when it
136
- * returns via the app's `returnUrl` (`expo-web-browser`). Absent ⇒ `signIn` is a
137
- * no-op.
138
- */
139
- readonly openAuthSession?: OpenAuthSession | null;
140
- /**
141
- * Opens a URL in a plain in-app browser (`expo-web-browser`'s `openBrowserAsync`) —
142
- * the **second** Connect door, for an app that declared no `returnUrl` (ADR-0052).
143
- * There is no return for an auth session to recognise, so the connect page mints
144
- * against the app links the Project lists and redirects to one; the code then
145
- * arrives on `linking`, exactly as a claimed Invite's does. Absent ⇒ `signIn` falls
146
- * back to the auth-session door alone.
147
- */
148
- readonly openBrowser?: OpenBrowser | null;
149
- /**
150
- * Closes the in-app browser this app opened (`expo-web-browser`'s `dismissBrowser`),
151
- * called once the deep link has landed so the person is returned to the app rather
152
- * than to the page they have finished with. Best-effort; absent ⇒ the browser is
153
- * left for the OS (and the person) to dismiss.
154
- */
155
- readonly dismissBrowser?: DismissBrowser | null;
156
- /**
157
- * Reads the deep link the app was opened with, and reports later ones — React
158
- * Native's `Linking`. It is how a Connect that started *outside* the app finishes
159
- * inside it: an invite email lands on the Fixback claim page, the claim redirects to
160
- * the app link the Project allowlists with a one-time code, and the app is launched
161
- * from the outside with no auth session in flight (ADR-0046). Absent ⇒ an inbound
162
- * Connect link is ignored and `signIn` remains the only way in — which on a gated
163
- * Project means an invited tester can never be recognised.
164
- */
165
- readonly linking?: LinkingLike | null;
166
- /** Reads the device environment (window size, OS description). */
167
- readonly environment?: () => EnvironmentInputs;
168
- /** Captures the current screen as a file reference; `null` on failure. */
169
- readonly captureScreenshot?: () => Promise<ScreenshotFile | null>;
170
- /** Releases a captured screenshot's tmpfile (best-effort, repeat-safe). */
171
- readonly releaseScreenshot?: (uri: string) => void;
172
- /**
173
- * Rasterises the screen to a replay frame file and resolves its URI (ADR-0044);
174
- * `null` on failure. Absent ⇒ no replay, whatever the Project and init asked for.
175
- */
176
- readonly captureReplayFrame?: (quality: number) => Promise<string | null>;
177
- /** Deletes a replay frame's (or encoded window's) file — best-effort, repeat-safe. */
178
- readonly releaseReplayFrame?: (uri: string) => void;
179
- /**
180
- * Encodes a window of frame files into one H.264/MP4
181
- * (`@fixback/expo-replay-encoder`). Absent, or resolving `null` — which is what a
182
- * build with no native encoder linked does — means the report ships without replay.
183
- */
184
- readonly encodeReplaySegment?: ReplayEncodeDeps["encodeSegment"];
185
- /** The `console` to wrap for the trace. `null` ⇒ skip. */
186
- readonly consoleObj?: ConsoleLike | null;
187
- /** The `XMLHttpRequest` to patch for the trace. `null` ⇒ skip. */
188
- readonly xhr?: XhrConstructor | null;
189
- /** The `ErrorUtils` seam for automatic error capture. `null` ⇒ skip. */
190
- readonly errorUtils?: ErrorUtilsLike | null;
191
- /** The `AppState` seam for occurrence flushes. */
192
- readonly appState?: AppStateLike | null;
193
- /**
194
- * Whether the app is in the foreground. Replay capture pauses when it is not —
195
- * a backgrounded app has nothing worth rasterising, and Android keeps JS timers
196
- * running. Absent ⇒ capture never pauses.
197
- */
198
- readonly isAppActive?: () => boolean;
199
- /** The transport seams. */
200
- readonly fetch?: FetchLike;
201
- readonly formData?: FormDataFactory;
202
- /** A development-only warning sink (silent in production builds). */
203
- readonly warn?: (message: string) => void;
204
- /** Clock source, injectable for tests. */
205
- readonly now?: () => number;
206
- }
207
- /** The client's lifecycle state. */
208
- export type FixbackStatus = "idle" | "starting" | "ready" | "disabled";
209
- /** What a `present` listener receives — the composer's opening context. */
210
- export interface ComposerContext {
211
- readonly screenshot: ScreenshotFile | null;
212
- }
213
- /** What the composer hands back when the Reporter sends. */
214
- export interface ComposerDraft {
215
- readonly comment?: string;
216
- /** The screenshot to attach (the presented one, unless removed). */
217
- readonly screenshot?: ScreenshotFile | null;
218
- }
219
- /** The headless client `FixbackProvider` drives. */
220
- export interface FixbackClient {
221
- /** Boot and arm capture. Idempotent; never throws. */
222
- start(): Promise<void>;
223
- getStatus(): FixbackStatus;
224
- /** The boot answer, once one arrived (for display; trust stays server-side). */
225
- getBoot(): BootAnswer | null;
226
- /** Whether a submission would be accepted right now. */
227
- canSubmit(): boolean;
228
- /** Subscribe to status changes. Returns an unsubscribe. */
229
- onStatus(listener: (status: FixbackStatus) => void): () => void;
230
- /**
231
- * Open the composer: capture a screenshot (unless disabled) and notify
232
- * `onPresent` listeners. A no-op while the client cannot submit.
233
- */
234
- present(): Promise<void>;
235
- /** Subscribe to composer-open requests. Returns an unsubscribe. */
236
- onPresent(listener: (context: ComposerContext) => void): () => void;
237
- /**
238
- * Record a screen change: a `navigation` trace crumb, and the screen the
239
- * next report's `url` names (spec 0004 §D).
240
- */
241
- trackScreen(name: string): void;
242
- /** Assemble, scrub, and submit a manual report. Never throws. */
243
- submit(draft: ComposerDraft): Promise<SubmitResult>;
244
- /** Release a captured screenshot's tmpfile (best-effort, repeat-safe). */
245
- discardScreenshot(screenshot: ScreenshotFile | null | undefined): void;
246
- /**
247
- * Drop the replay window frozen when the composer opened — call it when the Reporter
248
- * dismisses the composer, so the buffered frames stop being retained (ADR-0044).
249
- * A no-op when replay is off. Safe to call repeatedly.
250
- */
251
- discardReplay(): void;
252
- /** The resolved shake tuning for the provider to arm. */
253
- shakeConfig(): ResolvedShakeConfig;
254
- /**
255
- * Start a **Connect** (ADR-0066): open the platform's connect page in an in-app auth
256
- * session, bind the signed-in Account to this app as a Reporter session (stored in
257
- * SecureStore), and re-boot — so a gated Project's shake gesture arms once the Account
258
- * is eligible. Resolves to the resulting identity. Never throws.
259
- */
260
- signIn(): Promise<ReporterIdentity>;
261
- /** Clear the Reporter session — revoke it server-side, forget the token, re-boot. */
262
- signOut(): Promise<void>;
263
- /** The current Reporter identity — anonymous, or a connected Account with its tier. */
264
- identity(): ReporterIdentity;
265
- /** Subscribe to identity changes (boot, sign in, sign out). Returns an unsubscribe. */
266
- onIdentity(listener: (identity: ReporterIdentity) => void): () => void;
267
- /**
268
- * **Maintenance mode**'s notice while Fixback is down for maintenance (the maintenance-mode
269
- * ADR), else `null`. While it shows, the composer opens onto it with Send off; the client asks
270
- * boot again every minute and clears it once maintenance ends.
271
- */
272
- maintenance(): MaintenanceNotice | null;
273
- /** Subscribe to maintenance mode's notice coming and going. Returns an unsubscribe. */
274
- onMaintenance(listener: (notice: MaintenanceNotice | null) => void): () => void;
275
- /** Tear down every hook. Safe to call repeatedly. */
276
- destroy(): void;
277
- }
34
+ export type { ComposerContext, ComposerDraft, FixbackAdapters, FixbackClient, FixbackOptions, FixbackStatus, } from "./options";
278
35
  /** Create the headless client. See the module doc. */
279
36
  export declare function createFixbackClient(options: FixbackOptions, adapters?: FixbackAdapters): FixbackClient;