@fixback/expo 0.1.0 → 0.3.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 (58) hide show
  1. package/README.md +72 -8
  2. package/dist/FeedbackModal.d.ts +10 -2
  3. package/dist/FeedbackModal.js +84 -36
  4. package/dist/FeedbackModal.js.map +1 -0
  5. package/dist/FixbackProvider.d.ts +12 -0
  6. package/dist/FixbackProvider.js +46 -23
  7. package/dist/FixbackProvider.js.map +1 -0
  8. package/dist/adapters.js +73 -23
  9. package/dist/adapters.js.map +1 -0
  10. package/dist/breadcrumbs.d.ts +21 -222
  11. package/dist/breadcrumbs.js +45 -605
  12. package/dist/breadcrumbs.js.map +1 -0
  13. package/dist/client.d.ts +65 -67
  14. package/dist/client.js +288 -151
  15. package/dist/client.js.map +1 -0
  16. package/dist/connect.d.ts +60 -0
  17. package/dist/connect.js +63 -0
  18. package/dist/connect.js.map +1 -0
  19. package/dist/error-capture.d.ts +29 -79
  20. package/dist/error-capture.js +77 -295
  21. package/dist/error-capture.js.map +1 -0
  22. package/dist/http.d.ts +10 -25
  23. package/dist/http.js +15 -12
  24. package/dist/http.js.map +1 -0
  25. package/dist/identity.d.ts +11 -11
  26. package/dist/identity.js +23 -28
  27. package/dist/identity.js.map +1 -0
  28. package/dist/index.d.ts +6 -5
  29. package/dist/index.js +22 -5
  30. package/dist/index.js.map +1 -0
  31. package/dist/origin.d.ts +21 -0
  32. package/dist/origin.js +45 -0
  33. package/dist/origin.js.map +1 -0
  34. package/dist/package.json +3 -0
  35. package/dist/report.d.ts +28 -63
  36. package/dist/report.js +33 -25
  37. package/dist/report.js.map +1 -0
  38. package/dist/reporter-session-store.d.ts +27 -0
  39. package/dist/reporter-session-store.js +53 -0
  40. package/dist/reporter-session-store.js.map +1 -0
  41. package/dist/shake.d.ts +26 -0
  42. package/dist/shake.js +32 -7
  43. package/dist/shake.js.map +1 -0
  44. package/dist/submit.d.ts +24 -15
  45. package/dist/submit.js +30 -47
  46. package/dist/submit.js.map +1 -0
  47. package/dist/tokens.js +5 -1
  48. package/dist/tokens.js.map +1 -0
  49. package/dist/version.d.ts +1 -1
  50. package/dist/version.js +5 -1
  51. package/dist/version.js.map +1 -0
  52. package/package.json +14 -7
  53. package/dist/auto-report-backoff.d.ts +0 -42
  54. package/dist/auto-report-backoff.js +0 -67
  55. package/dist/boot.d.ts +0 -65
  56. package/dist/boot.js +0 -60
  57. package/dist/scrub.d.ts +0 -55
  58. package/dist/scrub.js +0 -184
package/dist/client.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /**
2
3
  * The headless Fixback client — everything the Expo SDK does except render.
3
4
  *
@@ -8,75 +9,58 @@
8
9
  * (`FixbackProvider`) is a thin shell over this: it feeds accelerometer
9
10
  * samples, listens for `present` events, and renders the composer.
10
11
  *
12
+ * `start()` is deliberately three named steps — {@link FixbackOptions} validation,
13
+ * `resolveIdentity`, then `armCapture` — rather than one long procedure, so the
14
+ * lifecycle reads as what it is and each step is testable on its own.
15
+ *
11
16
  * Every platform touchpoint is an injected adapter (`FixbackAdapters`), so the
12
17
  * whole orchestration is unit-tested without React Native. Every path is
13
18
  * defensive: a Fixback problem leaves the host app untouched — `start` resolves
14
19
  * to a `disabled` client instead of throwing.
15
20
  */
16
- import { requestBoot } from "./boot";
17
- import { createBreadcrumbBuffer, instrumentBreadcrumbs, navigationCrumb, } from "./breadcrumbs";
18
- import { installErrorCapture, } from "./error-capture";
19
- import { ensureAnonymousId } from "./identity";
20
- import { assembleContent, collectEnvironment, } from "./report";
21
- import { runBeforeSend } from "./scrub";
22
- import { DEFAULT_SHAKE_TUNING } from "./shake";
23
- import { submitReport, } from "./submit";
24
- import { EXPO_SDK_VERSION } from "./version";
25
- /** Where reports go when no `apiUrl` is configured (mirrors the web SDK). */
26
- export const DEFAULT_API_URL = "https://api.fixback.dev";
27
- /** How often the accelerometer reports while the gesture is armed. */
28
- export const DEFAULT_SHAKE_SAMPLE_INTERVAL_MS = 80;
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.DEFAULT_SHAKE_SAMPLE_INTERVAL_MS = exports.canonicalizeOrigin = exports.DEFAULT_API_URL = void 0;
23
+ exports.createFixbackClient = createFixbackClient;
24
+ const sdk_core_1 = require("@fixback/sdk-core");
25
+ Object.defineProperty(exports, "DEFAULT_API_URL", { enumerable: true, get: function () { return sdk_core_1.DEFAULT_API_URL; } });
26
+ const breadcrumbs_1 = require("./breadcrumbs");
27
+ const connect_1 = require("./connect");
28
+ const reporter_session_store_1 = require("./reporter-session-store");
29
+ const error_capture_1 = require("./error-capture");
30
+ const identity_1 = require("./identity");
31
+ const origin_1 = require("./origin");
32
+ Object.defineProperty(exports, "canonicalizeOrigin", { enumerable: true, get: function () { return origin_1.canonicalizeOrigin; } });
33
+ const report_1 = require("./report");
34
+ const shake_1 = require("./shake");
35
+ const submit_1 = require("./submit");
36
+ const version_1 = require("./version");
37
+ var shake_2 = require("./shake");
38
+ Object.defineProperty(exports, "DEFAULT_SHAKE_SAMPLE_INTERVAL_MS", { enumerable: true, get: function () { return shake_2.DEFAULT_SHAKE_SAMPLE_INTERVAL_MS; } });
29
39
  /** Normalise a base URL: trim and drop trailing slashes. */
30
40
  function normalizeBase(value) {
31
41
  return value.trim().replace(/\/+$/, "");
32
42
  }
33
- /**
34
- * Canonicalize the configured origin exactly as the server's `normalizeOrigin`
35
- * does when an allowlist entry is stored (`apps/api/src/projects/origin.ts`):
36
- * lowercase the scheme and host, elide default ports, and drop any userinfo,
37
- * path, query, or fragment. The server compares the `Origin` header by exact
38
- * string equality against those canonical rows — on the web the browser
39
- * serializes the header, but here the SDK is the serializer, so without this a
40
- * pasted `https://com.acme.MyApp` (bundle ids legally carry uppercase) would
41
- * silently never match its own allowlist entry. Implemented without `URL`
42
- * (React Native's polyfill is partial); a value that doesn't parse as an
43
- * http(s) origin is kept trimmed — boot will simply refuse it. IDN hosts are
44
- * not punycoded (the one divergence from the server; use the encoded form).
45
- */
46
- export function canonicalizeOrigin(value) {
47
- const trimmed = value.trim();
48
- const match = trimmed.match(/^(https?):\/\/([^/?#]+)(?:[/?#].*)?$/i);
49
- if (!match)
50
- return trimmed.replace(/\/+$/, "");
51
- const scheme = match[1].toLowerCase();
52
- let authority = match[2];
53
- const at = authority.lastIndexOf("@");
54
- if (at >= 0)
55
- authority = authority.slice(at + 1);
56
- let host = authority;
57
- let port;
58
- const portMatch = authority.match(/^(.+):(\d+)$/);
59
- if (portMatch) {
60
- host = portMatch[1];
61
- port = portMatch[2];
62
- }
63
- host = host.toLowerCase();
64
- const isDefaultPort = !port || (scheme === "https" && port === "443") || (scheme === "http" && port === "80");
65
- return isDefaultPort ? `${scheme}://${host}` : `${scheme}://${host}:${port}`;
66
- }
67
43
  /** Turn a screen name into the path segment of the report `url`. */
68
44
  function screenPath(name) {
69
45
  return name.trim().replace(/^\/+/, "");
70
46
  }
71
47
  /** Create the headless client. See the module doc. */
72
- export function createFixbackClient(options, adapters = {}) {
48
+ function createFixbackClient(options, adapters = {}) {
73
49
  const key = typeof options.key === "string" ? options.key.trim() : "";
74
- const origin = typeof options.origin === "string" ? canonicalizeOrigin(options.origin) : "";
75
- const apiUrl = normalizeBase(options.apiUrl || DEFAULT_API_URL) || DEFAULT_API_URL;
50
+ const origin = typeof options.origin === "string" ? (0, origin_1.canonicalizeOrigin)(options.origin) : "";
51
+ const apiUrl = normalizeBase(options.apiUrl || sdk_core_1.DEFAULT_API_URL) || sdk_core_1.DEFAULT_API_URL;
76
52
  const now = adapters.now ?? Date.now;
77
53
  const readEnvironment = adapters.environment ?? (() => ({}));
78
54
  const warn = adapters.warn ?? (() => { });
79
- const buffer = createBreadcrumbBuffer({
55
+ // The host app's Release (#117, ADR-0024), validated once here: an invalid value
56
+ // is dropped with a warning rather than shipped for the server to discard — and
57
+ // never a reason to refuse to start (capture must not break on config).
58
+ const release = options.release ? (0, sdk_core_1.normaliseRelease)(options.release) : undefined;
59
+ if (options.release && !release) {
60
+ warn(`Fixback: ignoring invalid release ${JSON.stringify(options.release)} — use 1-100 ` +
61
+ "visible characters with no whitespace or slashes (e.g. a git SHA or semver).");
62
+ }
63
+ const buffer = (0, sdk_core_1.createBreadcrumbBuffer)({
80
64
  beforeBreadcrumb: options.beforeBreadcrumb,
81
65
  now,
82
66
  });
@@ -86,6 +70,13 @@ export function createFixbackClient(options, adapters = {}) {
86
70
  let teardownTrace = null;
87
71
  let autoCapture = null;
88
72
  let identity = {};
73
+ // Connect state (ADR-0032): the Reporter session token held after a Connect, the
74
+ // signed-in Account and its live tier (for the composer's identity row), and where the
75
+ // SDK opens Connect — all set from the boot answer, refreshed on every boot.
76
+ let reporterSessionToken = null;
77
+ let account = null;
78
+ let currentTier = null;
79
+ let connectUrl;
89
80
  /**
90
81
  * The lifecycle generation. Each `start()` claims a new generation and every
91
82
  * await inside it re-checks that it is still current; `destroy()` bumps the
@@ -99,6 +90,7 @@ export function createFixbackClient(options, adapters = {}) {
99
90
  let running = false;
100
91
  const statusListeners = new Set();
101
92
  const presentListeners = new Set();
93
+ const identityListeners = new Set();
102
94
  function setStatus(next) {
103
95
  if (status === next)
104
96
  return;
@@ -118,9 +110,153 @@ export function createFixbackClient(options, adapters = {}) {
118
110
  return undefined;
119
111
  return currentScreen ? `${origin}/${screenPath(currentScreen)}` : origin;
120
112
  }
121
- /** Keep the SDK's own ingest traffic out of the trace. */
122
- function isFixbackApiRequest(url) {
123
- return url.startsWith(`${apiUrl}/api/ingest/`) || url.startsWith(`${apiUrl}/api/invites/`);
113
+ /** The transport deps every call shares: the injected seams plus this app's origin. */
114
+ function submitDeps() {
115
+ return { fetch: adapters.fetch, formData: adapters.formData, origin };
116
+ }
117
+ /**
118
+ * Step one of `start`: settle the identity evidence this session presents — the host
119
+ * app's own `hostIdentity`, the SDK-managed anonymous id (persisted when storage is
120
+ * available, ephemeral when it is not), and the **Reporter session** token from a
121
+ * prior Connect, loaded from SecureStore (ADR-0032). Boot re-derives the Account and
122
+ * tier from the token live; a revoked or expired one simply falls through to anonymous.
123
+ */
124
+ async function resolveIdentity() {
125
+ const anonymousId = options.anonymousId ?? (await (0, identity_1.ensureAnonymousId)(adapters.storage));
126
+ reporterSessionToken = await (0, reporter_session_store_1.readReporterSession)(adapters.secureStore, key);
127
+ return {
128
+ ...(options.hostIdentity ? { hostIdentity: options.hostIdentity } : {}),
129
+ ...(reporterSessionToken ? { reporterSession: reporterSessionToken } : {}),
130
+ anonymousId,
131
+ };
132
+ }
133
+ /** The current Reporter identity for the composer's identity row and `identity()`. */
134
+ function reporterIdentity() {
135
+ return account
136
+ ? {
137
+ status: "connected",
138
+ name: account.name,
139
+ email: account.email,
140
+ tier: currentTier ?? null,
141
+ }
142
+ : { status: "anonymous" };
143
+ }
144
+ /** Tell identity listeners the identity may have changed (boot / sign in / sign out). */
145
+ function notifyIdentity() {
146
+ const current = reporterIdentity();
147
+ for (const listener of identityListeners) {
148
+ try {
149
+ listener(current);
150
+ }
151
+ catch {
152
+ /* a listener must never break the client */
153
+ }
154
+ }
155
+ }
156
+ /** Tear down the trace + auto-capture hooks (best-effort), so they can be re-armed. */
157
+ function disarmCapture() {
158
+ try {
159
+ teardownTrace?.();
160
+ }
161
+ catch {
162
+ /* teardown is best-effort */
163
+ }
164
+ teardownTrace = null;
165
+ try {
166
+ autoCapture?.destroy();
167
+ }
168
+ catch {
169
+ /* teardown is best-effort */
170
+ }
171
+ autoCapture = null;
172
+ }
173
+ /**
174
+ * Boot with the current identity and reconcile capture + the identity row. Shared by
175
+ * `start`, `signIn`, and `signOut`: it swaps in the refreshed Reporter session token
176
+ * (30-day sliding), records the Account / tier / connect URL the boot answer carries,
177
+ * and arms capture only while a submission would be accepted — so a gated Project's
178
+ * shake gesture arms exactly when the Account becomes eligible, and drops when it signs
179
+ * out. `gen` is the caller's lifecycle generation; every await re-checks it so a
180
+ * destroyed (or restarted) client abandons this in-flight boot.
181
+ */
182
+ async function bootAndArm(gen) {
183
+ const answer = await (0, sdk_core_1.requestBoot)(apiUrl, { key, ...(0, sdk_core_1.compactIdentity)(identity) }, { fetch: adapters.fetch, origin });
184
+ if (gen !== generation)
185
+ return;
186
+ boot = answer;
187
+ // Swap in the refreshed session token (30-day sliding) when boot returned one.
188
+ if (answer?.reporterSession) {
189
+ reporterSessionToken = answer.reporterSession;
190
+ identity = { ...identity, reporterSession: reporterSessionToken };
191
+ await (0, reporter_session_store_1.writeReporterSession)(adapters.secureStore, key, reporterSessionToken);
192
+ if (gen !== generation)
193
+ return;
194
+ }
195
+ currentTier = answer?.tier ?? null;
196
+ account = answer?.account ?? null;
197
+ connectUrl = answer?.connectUrl;
198
+ notifyIdentity();
199
+ if (answer && answer.canSubmit) {
200
+ // Re-arm from a clean slate (the shared trace buffer persists across a re-arm, so
201
+ // no lead-up is lost) — a Connect that just made the Account eligible arms here.
202
+ disarmCapture();
203
+ armCapture(answer);
204
+ setStatus("ready");
205
+ }
206
+ else {
207
+ if (answer && !answer.originAllowed) {
208
+ warn(`Fixback: the Project has not allowlisted "${origin}" — add it to the Project's allowed origins to enable feedback from this app.`);
209
+ }
210
+ // Not eligible (a gated Project the Account cannot yet report on, or a signed-out
211
+ // visitor): nothing armed, but `connectUrl` is retained so `signIn` still works.
212
+ disarmCapture();
213
+ setStatus("disabled");
214
+ }
215
+ }
216
+ /**
217
+ * Step two of `start`: with a boot answer that says a submission would be
218
+ * accepted, install the trace instrumentation and (unless turned off) automatic
219
+ * error capture. Reaching here means the Gate is already satisfied, so an
220
+ * auto-error inherits the very tier boot derived.
221
+ */
222
+ function armCapture(answer) {
223
+ // Per-stream capture: an init option overrides the served config; an
224
+ // absent/malformed served value means ON (spec 0003 §C, ported).
225
+ const served = answer.capture;
226
+ const captureConsole = options.capture?.console ??
227
+ (typeof served?.console === "boolean" ? served.console : true);
228
+ const captureNetwork = options.capture?.network ??
229
+ (typeof served?.network === "boolean" ? served.network : true);
230
+ teardownTrace = (0, breadcrumbs_1.instrumentBreadcrumbs)(buffer, {
231
+ consoleObj: adapters.consoleObj,
232
+ xhr: adapters.xhr,
233
+ ignoreUrl: (url) => (0, sdk_core_1.isFixbackApiRequest)(apiUrl, url),
234
+ now,
235
+ captureConsole,
236
+ captureNetwork,
237
+ });
238
+ if (options.autoCapture === false)
239
+ return;
240
+ autoCapture = (0, error_capture_1.installErrorCapture)({
241
+ apiUrl,
242
+ key,
243
+ origin,
244
+ identity,
245
+ sdkVersion: version_1.EXPO_SDK_VERSION,
246
+ release,
247
+ deployEnvironment: options.environment,
248
+ buffer,
249
+ beforeSend: options.beforeSend,
250
+ scrub: options.scrub,
251
+ environment: readEnvironment,
252
+ currentUrl,
253
+ errorUtils: adapters.errorUtils,
254
+ appState: adapters.appState,
255
+ deps: {
256
+ submitReport: (url, input) => (0, submit_1.submitReport)(url, input, submitDeps()),
257
+ },
258
+ now,
259
+ });
124
260
  }
125
261
  async function start() {
126
262
  if (running)
@@ -129,6 +265,12 @@ export function createFixbackClient(options, adapters = {}) {
129
265
  const gen = ++generation;
130
266
  setStatus("starting");
131
267
  try {
268
+ // The master gate (`enabled: false`) keeps the SDK completely dormant: no
269
+ // identity, no boot call, nothing instrumented.
270
+ if (options.enabled === false) {
271
+ setStatus("disabled");
272
+ return;
273
+ }
132
274
  if (!key || !origin) {
133
275
  if (!key)
134
276
  warn("Fixback: init needs a publishable `key`; staying dormant.");
@@ -138,73 +280,91 @@ export function createFixbackClient(options, adapters = {}) {
138
280
  setStatus("disabled");
139
281
  return;
140
282
  }
141
- const anonymousId = options.anonymousId ?? (await ensureAnonymousId(adapters.storage));
283
+ identity = await resolveIdentity();
142
284
  if (gen !== generation)
143
285
  return; // destroyed (or restarted) while resolving
144
- identity = {
145
- ...(options.signedIdentity ? { signedIdentity: options.signedIdentity } : {}),
146
- ...(options.reporterId ? { reporterId: options.reporterId } : {}),
147
- anonymousId,
148
- };
149
- const answer = await requestBoot(apiUrl, { key, ...identity }, origin, adapters.fetchImpl);
150
- if (gen !== generation)
151
- return; // destroyed (or restarted) while booting
152
- boot = answer;
153
- if (!answer || !answer.canSubmit) {
154
- if (answer && !answer.originAllowed) {
155
- warn(`Fixback: the Project has not allowlisted "${origin}" — add it to the Project's allowed origins to enable feedback from this app.`);
156
- }
286
+ // Boot, then arm capture (and the shake gesture, via `ready`) only if a submission
287
+ // would be accepted; sets `disabled` otherwise, keeping `connectUrl` for `signIn`.
288
+ await bootAndArm(gen);
289
+ }
290
+ catch {
291
+ // A Fixback problem must never disturb the host app.
292
+ if (gen === generation)
157
293
  setStatus("disabled");
158
- return;
159
- }
160
- // Per-stream capture: an init option overrides the served config; an
161
- // absent/malformed served value means ON (spec 0003 §C, ported).
162
- const served = answer.capture;
163
- const captureConsole = options.capture?.console ??
164
- (typeof served?.console === "boolean" ? served.console : true);
165
- const captureNetwork = options.capture?.network ??
166
- (typeof served?.network === "boolean" ? served.network : true);
167
- teardownTrace = instrumentBreadcrumbs(buffer, {
168
- consoleObj: adapters.consoleObj,
169
- xhr: adapters.xhr,
170
- ignoreUrl: isFixbackApiRequest,
171
- now,
172
- captureConsole,
173
- captureNetwork,
294
+ }
295
+ }
296
+ /**
297
+ * Start a **Connect** (ADR-0032): open the platform's connect page in an in-app auth
298
+ * session, exchange the returned one-time code for a Reporter session, store it in
299
+ * SecureStore, and re-boot as the Account — so a gated Project's shake gesture arms
300
+ * once the Account is eligible. Exposed so a host app can put it on its own settings
301
+ * screen (the way a gated Project, which shows nothing to a signed-out visitor, is
302
+ * entered). Never throws: any failure leaves the current identity untouched.
303
+ */
304
+ async function signIn() {
305
+ const gen = generation;
306
+ if (!connectUrl)
307
+ return reporterIdentity(); // not booted, or an older server
308
+ if (!options.returnUrl) {
309
+ warn("Fixback: signIn needs a `returnUrl` (the scheme or universal link the connect page returns to) — set it in init.");
310
+ return reporterIdentity();
311
+ }
312
+ if (!adapters.openAuthSession) {
313
+ warn("Fixback: signIn needs an auth-session opener (install expo-web-browser).");
314
+ return reporterIdentity();
315
+ }
316
+ try {
317
+ const outcome = await (0, connect_1.startExpoConnect)(adapters.openAuthSession, {
318
+ connectUrl,
319
+ key,
320
+ returnUrl: options.returnUrl,
174
321
  });
175
- if (options.autoCapture !== false) {
176
- autoCapture = installErrorCapture({
177
- apiUrl,
322
+ if (gen !== generation)
323
+ return reporterIdentity();
324
+ if (outcome.kind === "code") {
325
+ const result = await (0, sdk_core_1.exchangeConnectCode)(apiUrl, {
178
326
  key,
179
- origin,
180
- identity,
181
- display: {
182
- name: options.reporterName,
183
- email: options.reporterEmail,
184
- },
185
- sdkVersion: EXPO_SDK_VERSION,
186
- buffer,
187
- beforeSend: options.beforeSend,
188
- scrub: options.scrub,
189
- environment: readEnvironment,
190
- currentUrl,
191
- errorUtils: adapters.errorUtils,
192
- appState: adapters.appState,
193
- deps: {
194
- submitReport: (url, input) => submitReport(url, input, {
195
- fetchImpl: adapters.fetchImpl,
196
- formData: adapters.formData,
197
- }),
198
- },
199
- now,
327
+ code: outcome.code,
328
+ anonymousId: identity.anonymousId,
329
+ platform: "expo",
330
+ }, { fetch: adapters.fetch });
331
+ if (gen !== generation)
332
+ return reporterIdentity();
333
+ if (result) {
334
+ reporterSessionToken = result.reporterSession;
335
+ identity = { ...identity, reporterSession: reporterSessionToken };
336
+ await (0, reporter_session_store_1.writeReporterSession)(adapters.secureStore, key, reporterSessionToken);
337
+ if (gen !== generation)
338
+ return reporterIdentity();
339
+ await bootAndArm(gen);
340
+ }
341
+ }
342
+ }
343
+ catch {
344
+ // A Connect problem must never disturb the host app.
345
+ }
346
+ return reporterIdentity();
347
+ }
348
+ /**
349
+ * Revoke the Reporter session, clear it from SecureStore, and re-boot as anonymous —
350
+ * so a gated Project's launcher (shake gesture) drops at once. Never throws.
351
+ */
352
+ async function signOut() {
353
+ const gen = generation;
354
+ try {
355
+ if (reporterSessionToken) {
356
+ await (0, sdk_core_1.revokeReporterSession)(apiUrl, reporterSessionToken, {
357
+ fetch: adapters.fetch,
200
358
  });
201
359
  }
202
- setStatus("ready");
360
+ await (0, reporter_session_store_1.clearReporterSession)(adapters.secureStore, key);
361
+ reporterSessionToken = null;
362
+ identity = { ...identity, reporterSession: undefined };
363
+ if (gen === generation)
364
+ await bootAndArm(gen);
203
365
  }
204
366
  catch {
205
367
  // A Fixback problem must never disturb the host app.
206
- if (gen === generation)
207
- setStatus("disabled");
208
368
  }
209
369
  }
210
370
  async function present() {
@@ -231,27 +391,20 @@ export function createFixbackClient(options, adapters = {}) {
231
391
  }
232
392
  async function submit(draft) {
233
393
  try {
234
- const content = runBeforeSend(
394
+ const content = (0, sdk_core_1.runBeforeSend)(
235
395
  // No Kind on the wire — analysis classifies the Issue server-side
236
396
  // (ADR-0023).
237
- assembleContent({
397
+ (0, report_1.assembleContent)({
238
398
  comment: draft.comment,
239
399
  url: currentUrl(),
240
- environment: collectEnvironment(readEnvironment(), EXPO_SDK_VERSION),
400
+ environment: (0, report_1.collectEnvironment)(readEnvironment(), version_1.EXPO_SDK_VERSION, release),
401
+ deployEnvironment: options.environment,
241
402
  trace: buffer.snapshot(),
242
- reporterName: options.reporterName,
243
- reporterEmail: options.reporterEmail,
244
403
  }), { hook: options.beforeSend, scrub: options.scrub });
245
404
  // Dropped by the project's own beforeSend — an intentional non-send.
246
405
  if (!content)
247
406
  return { ok: true, feedback: null };
248
- const result = await submitReport(apiUrl, {
249
- key,
250
- origin,
251
- identity,
252
- content,
253
- screenshot: draft.screenshot ?? null,
254
- }, { fetchImpl: adapters.fetchImpl, formData: adapters.formData });
407
+ const result = await (0, submit_1.submitReport)(apiUrl, { key, identity, content, screenshot: draft.screenshot ?? null }, submitDeps());
255
408
  // A delivered screenshot's tmpfile has served its purpose — release it.
256
409
  // On a failure the file is kept: the composer holds the draft for retry.
257
410
  if (result.ok)
@@ -287,25 +440,13 @@ export function createFixbackClient(options, adapters = {}) {
287
440
  currentScreen = trimmed;
288
441
  const to = currentUrl();
289
442
  if (from && to && from !== to) {
290
- buffer.add(navigationCrumb(from, to, now()));
443
+ buffer.add((0, sdk_core_1.navigationCrumb)(from, to, now()));
291
444
  }
292
445
  }
293
446
  catch {
294
447
  /* never throw into the host app */
295
448
  }
296
449
  }
297
- function shakeConfig() {
298
- const shake = options.shake ?? {};
299
- return {
300
- enabled: shake.enabled !== false,
301
- sampleIntervalMs: shake.sampleIntervalMs ?? DEFAULT_SHAKE_SAMPLE_INTERVAL_MS,
302
- thresholdG: shake.thresholdG ?? DEFAULT_SHAKE_TUNING.thresholdG,
303
- minPeaks: shake.minPeaks ?? DEFAULT_SHAKE_TUNING.minPeaks,
304
- windowMs: shake.windowMs ?? DEFAULT_SHAKE_TUNING.windowMs,
305
- minGapMs: shake.minGapMs ?? DEFAULT_SHAKE_TUNING.minGapMs,
306
- cooldownMs: shake.cooldownMs ?? DEFAULT_SHAKE_TUNING.cooldownMs,
307
- };
308
- }
309
450
  return {
310
451
  start,
311
452
  getStatus: () => status,
@@ -323,29 +464,25 @@ export function createFixbackClient(options, adapters = {}) {
323
464
  trackScreen,
324
465
  submit,
325
466
  discardScreenshot,
326
- shakeConfig,
467
+ shakeConfig: () => (0, shake_1.resolveShakeConfig)(options.shake),
468
+ signIn,
469
+ signOut,
470
+ identity: () => reporterIdentity(),
471
+ onIdentity(listener) {
472
+ identityListeners.add(listener);
473
+ return () => identityListeners.delete(listener);
474
+ },
327
475
  destroy() {
328
476
  // Abandon any in-flight start and allow a later start() to re-arm —
329
477
  // React StrictMode replays the provider's effect on this same instance.
330
478
  generation += 1;
331
479
  running = false;
332
- try {
333
- teardownTrace?.();
334
- }
335
- catch {
336
- /* teardown is best-effort */
337
- }
338
- teardownTrace = null;
339
- try {
340
- autoCapture?.destroy();
341
- }
342
- catch {
343
- /* teardown is best-effort */
344
- }
345
- autoCapture = null;
480
+ disarmCapture();
346
481
  statusListeners.clear();
347
482
  presentListeners.clear();
483
+ identityListeners.clear();
348
484
  setStatus("disabled");
349
485
  },
350
486
  };
351
487
  }
488
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The Expo **Connect** flow (ADR-0032) — the React Native half of the round trip whose
3
+ * pure pieces (URL building, the code exchange, the storage-key format) live in
4
+ * `@fixback/sdk-core`.
5
+ *
6
+ * Where the browser opens the platform's connect page in a popup, Expo opens it in an
7
+ * **in-app auth session** (`WebBrowser.openAuthSessionAsync`, injected here as
8
+ * {@link OpenAuthSession}) — the standard mobile pattern: a secure system browser that
9
+ * shares no cookies with the app, signs the person in, and hands control back when the
10
+ * page redirects to the app's own `returnUrl` (a custom scheme or a universal link the
11
+ * host declares once in `init`). The connect page returns the one-time code on that URL's
12
+ * `?fixback=` query parameter, exactly as the web's redirect fallback does; the SDK reads
13
+ * it here and exchanges it for a Reporter session.
14
+ */
15
+ /**
16
+ * The result an auth-session opener resolves to — `WebBrowser.openAuthSessionAsync`'s
17
+ * shape, pared to what Connect reads. `type` is `"success"` when the session returned
18
+ * via `returnUrl` (then `url` carries it); `"cancel"` / `"dismiss"` when the person
19
+ * closed it. Other types are treated as a cancel.
20
+ */
21
+ export interface AuthSessionResult {
22
+ readonly type: string;
23
+ readonly url?: string;
24
+ }
25
+ /**
26
+ * Open an in-app auth session at `url` and resolve when it returns to `returnUrl` (or
27
+ * the person dismisses it). `expo-web-browser`'s `openAuthSessionAsync(url, returnUrl)`,
28
+ * injected so the client is unit-tested without the native module.
29
+ */
30
+ export type OpenAuthSession = (url: string, returnUrl: string) => Promise<AuthSessionResult>;
31
+ /**
32
+ * The outcome of starting Connect: `code` when the auth session returned a one-time
33
+ * code, or `cancelled` when the person dismissed it or it came back without one.
34
+ */
35
+ export type ExpoConnectOutcome = {
36
+ readonly kind: "code";
37
+ readonly code: string;
38
+ } | {
39
+ readonly kind: "cancelled";
40
+ };
41
+ /**
42
+ * Read the Connect return code off a returned URL's `?fixback=` parameter, or `null`.
43
+ * Parsed by hand rather than via `URL`/`URLSearchParams` so it works on every React
44
+ * Native runtime (Hermes' URL support is partial), the same DOM-free posture as
45
+ * `@fixback/sdk-core`. Handles the parameter appearing first (`?fixback=`) or later
46
+ * (`&fixback=`), and stops at the next `&` or `#`.
47
+ */
48
+ export declare function parseReturnCode(url: string): string | null;
49
+ /**
50
+ * Start Connect: open the connect page in an in-app auth session and resolve to the
51
+ * one-time code it returns via `returnUrl`, or `cancelled`. `returnUrl` is where the
52
+ * person is sent back — validated server-side against the Project's allowed origins, so
53
+ * a hostile app cannot redirect the code elsewhere. Never throws: any failure resolves
54
+ * to `cancelled`, so a Connect problem never surfaces on the host app.
55
+ */
56
+ export declare function startExpoConnect(openAuthSession: OpenAuthSession, params: {
57
+ connectUrl: string;
58
+ key: string;
59
+ returnUrl: string;
60
+ }): Promise<ExpoConnectOutcome>;