@fixback/expo 0.8.3 → 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.
Files changed (46) hide show
  1. package/README.md +5 -7
  2. package/dist/FeedbackModal.d.ts +7 -2
  3. package/dist/FeedbackModal.js +30 -3
  4. package/dist/FeedbackModal.js.map +1 -1
  5. package/dist/FixbackProvider.js +5 -1
  6. package/dist/FixbackProvider.js.map +1 -1
  7. package/dist/adapters.js +5 -4
  8. package/dist/adapters.js.map +1 -1
  9. package/dist/breadcrumbs.d.ts +15 -51
  10. package/dist/breadcrumbs.js +22 -169
  11. package/dist/breadcrumbs.js.map +1 -1
  12. package/dist/client-boot.d.ts +40 -0
  13. package/dist/client-boot.js +269 -0
  14. package/dist/client-boot.js.map +1 -0
  15. package/dist/client-connect.d.ts +33 -0
  16. package/dist/client-connect.js +306 -0
  17. package/dist/client-connect.js.map +1 -0
  18. package/dist/client-replay.d.ts +31 -0
  19. package/dist/client-replay.js +165 -0
  20. package/dist/client-replay.js.map +1 -0
  21. package/dist/client-state.d.ts +131 -0
  22. package/dist/client-state.js +158 -0
  23. package/dist/client-state.js.map +1 -0
  24. package/dist/client.d.ts +10 -245
  25. package/dist/client.js +80 -787
  26. package/dist/client.js.map +1 -1
  27. package/dist/error-capture.js +6 -26
  28. package/dist/error-capture.js.map +1 -1
  29. package/dist/identity.d.ts +0 -2
  30. package/dist/identity.js +0 -3
  31. package/dist/identity.js.map +1 -1
  32. package/dist/options.d.ts +263 -0
  33. package/dist/options.js +12 -0
  34. package/dist/options.js.map +1 -0
  35. package/dist/report.d.ts +13 -12
  36. package/dist/report.js +19 -38
  37. package/dist/report.js.map +1 -1
  38. package/dist/submit.d.ts +10 -37
  39. package/dist/submit.js +14 -64
  40. package/dist/submit.js.map +1 -1
  41. package/dist/tokens.d.ts +8 -3
  42. package/dist/tokens.js +12 -6
  43. package/dist/tokens.js.map +1 -1
  44. package/dist/version.d.ts +1 -1
  45. package/dist/version.js +1 -1
  46. package/package.json +4 -3
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ /**
3
+ * The state the headless client's pieces share (#661): one explicit object, where
4
+ * `createFixbackClient` used to be a single closure whose parts reached into each
5
+ * other's variables.
6
+ *
7
+ * It holds what the client resolves from its options once, at creation — the trimmed
8
+ * key, the canonical origin, the API base, the validated Release, the Correlation id's
9
+ * targets, the trace buffer, the listener sets — and every value that changes over the
10
+ * client's life: the status and the boot answer, the lifecycle generation, the Connect
11
+ * state, the armed capture, and maintenance mode's notice. Every piece is created over
12
+ * the same object — boot and capture arming (`./client-boot`), mobile replay
13
+ * (`./client-replay`), Connect (`./client-connect`), and the composer-facing surface
14
+ * (`./client`) — so what one piece writes is what the next one reads.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.createClientState = createClientState;
18
+ exports.setStatus = setStatus;
19
+ exports.currentUrl = currentUrl;
20
+ exports.submitDeps = submitDeps;
21
+ exports.reporterIdentity = reporterIdentity;
22
+ exports.notifyIdentity = notifyIdentity;
23
+ const sdk_core_1 = require("@fixback/sdk-core");
24
+ const origin_1 = require("./origin");
25
+ const propagation_1 = require("./propagation");
26
+ /** Normalise a base URL: trim and drop trailing slashes. */
27
+ function normalizeBase(value) {
28
+ return value.trim().replace(/\/+$/, "");
29
+ }
30
+ /** Turn a screen name into the path segment of the report `url`. */
31
+ function screenPath(name) {
32
+ return name.trim().replace(/^\/+/, "");
33
+ }
34
+ /**
35
+ * Resolve a client's options once, saying what it cannot use as it goes, and set up its
36
+ * state at rest: `idle`, nothing armed, no boot answer yet.
37
+ */
38
+ function createClientState(options, adapters) {
39
+ const key = typeof options.key === "string" ? options.key.trim() : "";
40
+ const origin = typeof options.origin === "string" ? (0, origin_1.canonicalizeOrigin)(options.origin) : "";
41
+ const apiUrl = normalizeBase(options.apiUrl || sdk_core_1.DEFAULT_API_URL) || sdk_core_1.DEFAULT_API_URL;
42
+ const now = adapters.now ?? Date.now;
43
+ const readEnvironment = adapters.environment ?? (() => ({}));
44
+ const warn = adapters.warn ?? (() => { });
45
+ // The host app's Release (#117, ADR-0024), validated once here: an invalid value
46
+ // is dropped with a warning rather than shipped for the server to discard — and
47
+ // never a reason to refuse to start (capture must not break on config). Unlike the
48
+ // browser SDK, nothing falls back to a bundler-injected Release: with no option,
49
+ // none is stamped.
50
+ const { release, warning: releaseWarning } = (0, sdk_core_1.readReleaseOption)(options.release);
51
+ if (releaseWarning)
52
+ warn(`${sdk_core_1.WARN_PREFIX} ${releaseWarning}`);
53
+ // `environment` is no longer an option — the key selects the Environment (ADR
54
+ // 2026-09-24) — but a plain-JS caller gets no type error for passing it, and would
55
+ // otherwise believe it was honoured. Said once, here, never per report.
56
+ const removedOption = (0, sdk_core_1.removedEnvironmentOptionWarning)(options);
57
+ if (removedOption)
58
+ warn(`${sdk_core_1.WARN_PREFIX} ${removedOption}`);
59
+ // The Correlation id's targets (#540) — app topology known in code, so an init option
60
+ // rather than a served setting, and allowlist-only on mobile. An entry that is not a
61
+ // bare origin is never used, and said so once, the way an invalid release is — in the
62
+ // words every capture SDK shares.
63
+ const propagation = (0, propagation_1.normalizePropagateTo)(options.propagateTo);
64
+ const ignoredPropagateTo = (0, sdk_core_1.ignoredPropagateToWarning)(propagation.rejected);
65
+ if (ignoredPropagateTo)
66
+ warn(`${sdk_core_1.WARN_PREFIX} ${ignoredPropagateTo}`);
67
+ const buffer = (0, sdk_core_1.createBreadcrumbBuffer)({
68
+ beforeBreadcrumb: options.beforeBreadcrumb,
69
+ now,
70
+ });
71
+ return {
72
+ options,
73
+ adapters,
74
+ key,
75
+ origin,
76
+ apiUrl,
77
+ now,
78
+ readEnvironment,
79
+ warn,
80
+ release,
81
+ propagateTo: propagation.origins,
82
+ buffer,
83
+ statusListeners: new Set(),
84
+ presentListeners: new Set(),
85
+ identityListeners: new Set(),
86
+ maintenanceListeners: new Set(),
87
+ status: "idle",
88
+ boot: null,
89
+ bootSequence: 0,
90
+ currentScreen: null,
91
+ teardownTrace: null,
92
+ autoCapture: null,
93
+ replayRecorder: null,
94
+ pendingReplayWindow: null,
95
+ identity: {},
96
+ reporterSessionToken: null,
97
+ account: null,
98
+ currentTier: null,
99
+ connectUrl: undefined,
100
+ teardownLinking: null,
101
+ lastInboundCode: null,
102
+ inboundWaiters: [],
103
+ generation: 0,
104
+ running: false,
105
+ maintenance: null,
106
+ maintenanceRecheck: (0, sdk_core_1.createMaintenanceRecheck)(),
107
+ lastDormancyWarning: null,
108
+ };
109
+ }
110
+ /** Move the client to `next`, telling every status listener once. */
111
+ function setStatus(s, next) {
112
+ if (s.status === next)
113
+ return;
114
+ s.status = next;
115
+ for (const listener of s.statusListeners) {
116
+ try {
117
+ listener(next);
118
+ }
119
+ catch {
120
+ /* a listener must never break the client */
121
+ }
122
+ }
123
+ }
124
+ /**
125
+ * The `url` a report carries right now (spec 0004 §D). With an `origin` configured
126
+ * it reads `<origin>/<screen>`; without one — the default since ADR-0041 — the
127
+ * screen stands alone, so the Evidence still says *where* the reporter was rather
128
+ * than losing it to a setting nobody has to fill in. Ingest takes `url` as a free
129
+ * string and the dashboard renders it as text, so a bare screen name is honest
130
+ * here where a synthesised origin would be fiction.
131
+ */
132
+ function currentUrl(s) {
133
+ const { origin, currentScreen } = s;
134
+ if (!origin)
135
+ return currentScreen ? screenPath(currentScreen) : undefined;
136
+ return currentScreen ? `${origin}/${screenPath(currentScreen)}` : origin;
137
+ }
138
+ /** The transport deps every call shares: the injected seams plus this app's origin. */
139
+ function submitDeps(s) {
140
+ return { fetch: s.adapters.fetch, formData: s.adapters.formData, origin: s.origin };
141
+ }
142
+ /** The current Reporter identity for the composer's identity row and `identity()`. */
143
+ function reporterIdentity(s) {
144
+ return (0, sdk_core_1.reporterIdentityOf)(s.account, s.currentTier);
145
+ }
146
+ /** Tell identity listeners the identity may have changed (boot / sign in / sign out). */
147
+ function notifyIdentity(s) {
148
+ const current = reporterIdentity(s);
149
+ for (const listener of s.identityListeners) {
150
+ try {
151
+ listener(current);
152
+ }
153
+ catch {
154
+ /* a listener must never break the client */
155
+ }
156
+ }
157
+ }
158
+ //# sourceMappingURL=client-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-state.js","sourceRoot":"","sources":["../src/client-state.ts"],"names":[],"mappings":"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"}
package/dist/client.d.ts CHANGED
@@ -12,260 +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 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
- /** Tear down every hook. Safe to call repeatedly. */
268
- destroy(): void;
269
- }
34
+ export type { ComposerContext, ComposerDraft, FixbackAdapters, FixbackClient, FixbackOptions, FixbackStatus, } from "./options";
270
35
  /** Create the headless client. See the module doc. */
271
36
  export declare function createFixbackClient(options: FixbackOptions, adapters?: FixbackAdapters): FixbackClient;