@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/adapters.js +5 -4
- package/dist/adapters.js.map +1 -1
- package/dist/breadcrumbs.d.ts +15 -51
- package/dist/breadcrumbs.js +22 -169
- package/dist/breadcrumbs.js.map +1 -1
- package/dist/client-boot.d.ts +40 -0
- package/dist/client-boot.js +269 -0
- package/dist/client-boot.js.map +1 -0
- package/dist/client-connect.d.ts +33 -0
- package/dist/client-connect.js +306 -0
- package/dist/client-connect.js.map +1 -0
- package/dist/client-replay.d.ts +31 -0
- package/dist/client-replay.js +165 -0
- package/dist/client-replay.js.map +1 -0
- package/dist/client-state.d.ts +131 -0
- package/dist/client-state.js +158 -0
- package/dist/client-state.js.map +1 -0
- package/dist/client.d.ts +10 -253
- package/dist/client.js +73 -865
- package/dist/client.js.map +1 -1
- package/dist/error-capture.js +6 -26
- package/dist/error-capture.js.map +1 -1
- package/dist/identity.d.ts +0 -2
- package/dist/identity.js +0 -3
- package/dist/identity.js.map +1 -1
- package/dist/options.d.ts +263 -0
- package/dist/options.js +12 -0
- package/dist/options.js.map +1 -0
- package/dist/report.d.ts +13 -12
- package/dist/report.js +19 -38
- package/dist/report.js.map +1 -1
- package/dist/submit.d.ts +10 -46
- package/dist/submit.js +14 -79
- package/dist/submit.js.map +1 -1
- package/dist/tokens.d.ts +5 -3
- package/dist/tokens.js +8 -6
- package/dist/tokens.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -3
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 {
|
|
21
|
-
import {
|
|
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;
|