@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.
- package/README.md +72 -8
- package/dist/FeedbackModal.d.ts +10 -2
- package/dist/FeedbackModal.js +84 -36
- package/dist/FeedbackModal.js.map +1 -0
- package/dist/FixbackProvider.d.ts +12 -0
- package/dist/FixbackProvider.js +46 -23
- package/dist/FixbackProvider.js.map +1 -0
- package/dist/adapters.js +73 -23
- package/dist/adapters.js.map +1 -0
- package/dist/breadcrumbs.d.ts +21 -222
- package/dist/breadcrumbs.js +45 -605
- package/dist/breadcrumbs.js.map +1 -0
- package/dist/client.d.ts +65 -67
- package/dist/client.js +288 -151
- package/dist/client.js.map +1 -0
- package/dist/connect.d.ts +60 -0
- package/dist/connect.js +63 -0
- package/dist/connect.js.map +1 -0
- package/dist/error-capture.d.ts +29 -79
- package/dist/error-capture.js +77 -295
- package/dist/error-capture.js.map +1 -0
- package/dist/http.d.ts +10 -25
- package/dist/http.js +15 -12
- package/dist/http.js.map +1 -0
- package/dist/identity.d.ts +11 -11
- package/dist/identity.js +23 -28
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +22 -5
- package/dist/index.js.map +1 -0
- package/dist/origin.d.ts +21 -0
- package/dist/origin.js +45 -0
- package/dist/origin.js.map +1 -0
- package/dist/package.json +3 -0
- package/dist/report.d.ts +28 -63
- package/dist/report.js +33 -25
- package/dist/report.js.map +1 -0
- package/dist/reporter-session-store.d.ts +27 -0
- package/dist/reporter-session-store.js +53 -0
- package/dist/reporter-session-store.js.map +1 -0
- package/dist/shake.d.ts +26 -0
- package/dist/shake.js +32 -7
- package/dist/shake.js.map +1 -0
- package/dist/submit.d.ts +24 -15
- package/dist/submit.js +30 -47
- package/dist/submit.js.map +1 -0
- package/dist/tokens.js +5 -1
- package/dist/tokens.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +5 -1
- package/dist/version.js.map +1 -0
- package/package.json +14 -7
- package/dist/auto-report-backoff.d.ts +0 -42
- package/dist/auto-report-backoff.js +0 -67
- package/dist/boot.d.ts +0 -65
- package/dist/boot.js +0 -60
- package/dist/scrub.d.ts +0 -55
- 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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
122
|
-
function
|
|
123
|
-
return
|
|
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
|
-
|
|
283
|
+
identity = await resolveIdentity();
|
|
142
284
|
if (gen !== generation)
|
|
143
285
|
return; // destroyed (or restarted) while resolving
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (gen
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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 (
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
180
|
-
identity,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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>;
|