@guidelytech/react-native 0.15.0 → 0.17.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 CHANGED
@@ -76,7 +76,7 @@ tasks).
76
76
 
77
77
  | | |
78
78
  |---|---|
79
- | `useGuidely()` | Client: `identify(id, traits?, account?)`, `setAccount`, `updateTraits`, `reset()`, `track(name, props?)`, `screen(name)`, `startGuide(idOrKey)`, `stopGuide()`, `dismissGuide()`, `openChecklist(key)`, `closeChecklist(key)`, `completeChecklistTask(key, taskId)`, `openResourceCenter()`, `closeResourceCenter()`, `refresh()`, `setConsent()`, `on(event, cb)`, `getGuides()`, `getActiveGuide()`, `getState()`, `startSessionRecording()`, `stopSessionRecording()`, `isRecording()`, `getSessionId()` |
79
+ | `useGuidely()` | Client: `identify(id, traits?, account?)`, `setAccount`, `updateTraits`, `reset()`, `track(name, props?)`, `screen(name)`, `startGuide(idOrKey)`, `stopGuide()`, `dismissGuide()`, `openChecklist(key)`, `closeChecklist(key)`, `completeChecklistTask(key, taskId)`, `openResourceCenter()`, `closeResourceCenter()`, `openArticle(articleId)`, `refresh()`, `setConsent()`, `on(event, cb)`, `getGuides()`, `getActiveGuide()`, `getState()`, `startSessionRecording()`, `stopSessionRecording()`, `isRecording()`, `getSessionId()`, `captureException(err, ctx?)`, `captureMessage(msg, level?)`, `addBreadcrumb()`, `setTag(s)`, `setExtra(s)`, `setContext()`, `setUser()` |
80
80
  | `useGuidelyReady()` | `true` once config is loaded |
81
81
  | `useGuidelyIdentify(id, traits?, account?)` | Identify when the signed-in user changes |
82
82
  | `useGuidelyScreen(name)` | Report a screen view from a component |
@@ -91,7 +91,8 @@ Provider props: `projectId`, `apiUrl`, `environment`, `storage`, `user`, `debug`
91
91
  `consent` (`granted` / `pending` / `denied`), `appVersion` (overrides the detected version, see below), `theme`,
92
92
  `configRefreshMs`, `onNavigate`, `onError`, `showChecklistLauncher` (default `true`),
93
93
  `showResourceCenterLauncher` (default `false`), `showMessengerLauncher` (default `true`), `insets`,
94
- `launcherOffset` (default 72), `autocapture`, `appBuild`, `sessionReplay`.
94
+ `launcherOffset` (default 72), `autocapture`, `appBuild`, `sessionReplay`, `release`, `errors`
95
+ (see [Errors and crashes](#errors-and-crashes)).
95
96
 
96
97
  ## Product analytics (autocapture)
97
98
 
@@ -165,6 +166,122 @@ native-stack headers, some third-party native views) may not reach JavaScript to
165
166
  then not captured. Coordinates are relative to the React Native root view (the window in a
166
167
  full-screen app).
167
168
 
169
+ ## Errors and crashes
170
+
171
+ The SDK reports JavaScript errors with the same capture API as Sentry's SDKs, to the project's
172
+ **Errors** page. Uncaught errors and unhandled promise rejections are captured automatically; caught
173
+ ones with `captureException`.
174
+
175
+ ```tsx
176
+ const guidely = useGuidely();
177
+ try {
178
+ await pay(cart);
179
+ } catch (err) {
180
+ guidely.captureException(err, { tags: { area: "checkout" }, extra: { cartSize: cart.length }, level: "error", fingerprint: ["{{ default }}", "checkout"], contexts: { payment: { provider: "stripe" } } });
181
+ }
182
+ guidely.captureMessage("Checkout took 30 s", "warning");
183
+ guidely.setTag("plan", "pro"); // setTags, setExtra, setExtras, setContext(name, ctx | null)
184
+ guidely.setUser({ id: "u_42", email: "ana@example.com" }); // identify() + email/username on errors; null = reset()
185
+ guidely.addBreadcrumb({ category: "cart", message: "Added item", data: { sku: "A1" } });
186
+ ```
187
+
188
+ ```tsx
189
+ <GuidelyProvider
190
+ release="2.4.1" // default: the app version; must match --release of the source-map upload
191
+ errors={{
192
+ sampleRate: 1, // 0–1, crashes included
193
+ ignoreErrors: ["Network request failed", /^AbortError: /],
194
+ denyUrls: [], allowUrls: [], // against the innermost frame's file (app:///index.android.bundle)
195
+ beforeSend: ({ properties, detail }) => (properties.message.includes("ignore") ? null : { properties, detail }),
196
+ beforeBreadcrumb: (crumb) => (crumb.category === "console" ? null : crumb),
197
+ breadcrumbs: { console: true, http: true, navigation: true, taps: true, appLifecycle: true, guides: true }, // or false
198
+ captureErrors: true, // false: no automatic capture (captureException still works)
199
+ dist: "512", // default: the app build number
200
+ }}
201
+ …
202
+ >
203
+ ```
204
+
205
+ Errors need the project's error tracking on (Settings → Privacy & data), `analytics` and consent:
206
+ nothing leaves the device before consent is granted, errors captured while consent is `pending` are
207
+ sent once it is granted and deleted when it is denied. The same error is sent at most 5 times per
208
+ launch, and at most 50 errors per session (crashes always go out).
209
+
210
+ **What is captured.** `ErrorUtils.setGlobalHandler` reports every uncaught JS error and then calls the
211
+ handler that was installed before (React Native's own, which shows the red box in development and
212
+ ends the app in release builds), with the same arguments, so nothing changes about how your app
213
+ crashes. Fatal errors are `level: "fatal"`, `handled: false`, `mechanism: "onerror"`; non-fatal ones
214
+ `level: "error"`. Unhandled promise rejections (`mechanism: "unhandledrejection"`, `level: "error"`)
215
+ come from Hermes' rejection tracker. On JavaScriptCore pass React Native's Promise polyfill tracker:
216
+ `errors={{ rejectionTracker: require("promise/setimmediate/rejection-tracking") }}`. In development
217
+ the SDK keeps React Native's "Possible unhandled promise rejection" warning.
218
+
219
+ **Crashes are saved first.** A fatal error ends a release app right after the handler, so the report
220
+ is written to `storage` the moment it happens and sent on the next launch (which starts a new
221
+ session; the crash keeps the session that crashed). React Native's handler is held back until the
222
+ report is written and, when the network allows, sent — at most 2 s (`FATAL_FLUSH_MS`); in
223
+ development it runs at once. AsyncStorage writes are asynchronous, so a report can still be lost if
224
+ the OS ends the process before the write lands; a synchronous `storage` (an MMKV adapter) always has
225
+ it. Without `storage` crash reports can't outlive the crash.
226
+
227
+ **Breadcrumbs** (the last 100 ride along with each error): `navigation` (`screen()`), `ui.click`
228
+ (taps, with the element's `selector`; never its text), `app.lifecycle` (`AppState`), `console`
229
+ (`console.warn` / `console.error`), `fetch` / `xhr` (method, URL without query, status, duration;
230
+ never Guidely's own requests; React Native's `fetch` runs on XMLHttpRequest and is recorded once, as
231
+ `fetch`) and `guide` (guides shown, stepped through, completed, dismissed). Tap breadcrumbs need the
232
+ provider's touch-observing wrapper (not with `autocapture={false}`).
233
+
234
+ **Each event** carries `properties` (type, message, the innermost 30 frames, `platform: "javascript"`,
235
+ `release`) and a Sentry-shaped `detail` (≤ 64 KB): the `cause` chain, breadcrumbs, tags, extra, user
236
+ and contexts `os`, `device` (model from `expo-device` / `react-native-device-info`, or Android's own
237
+ constants), `app` (version, build, foreground), `runtime` (`hermes` / `jsc`) and
238
+ `react_native_context` (engine, React Native version, new architecture, Expo). Keys that look like
239
+ credentials (`password`, `token`, `authorization`, `cookie`, …) are replaced with `[Filtered]`.
240
+
241
+ **Stack traces.** Hermes release frames (`at pay (address at index.android.bundle:1:9876)`: line 1,
242
+ column = bytecode offset) and JavaScriptCore frames (`pay@…/MyApp.app/main.jsbundle:1:2345`) are
243
+ reported against `app:///index.android.bundle` / `app:///main.jsbundle`, whatever folder the app is
244
+ installed in, so they map through source maps uploaded with `--url-prefix /`. Metro dev-server frames
245
+ keep their URL. Engine built-ins (`(native)`, `InternalBytecode.js`) and `node_modules` frames are
246
+ marked not in-app.
247
+
248
+ ### Uploading Hermes source maps
249
+
250
+ Hermes needs the *composed* map (Metro's map combined with the Hermes bytecode map). Put the bundle and
251
+ its map side by side in one folder and upload with the same release the app reports:
252
+
253
+ ```bash
254
+ # Android: the release build composes the map; copy both next to each other
255
+ mkdir -p build/sourcemaps
256
+ cp android/app/build/generated/assets/react/release/index.android.bundle build/sourcemaps/
257
+ cp android/app/build/generated/sourcemaps/react/release/index.android.bundle.map build/sourcemaps/
258
+
259
+ # Or by hand (Android shown; iOS uses --platform ios and main.jsbundle)
260
+ npx react-native bundle --platform android --dev false --entry-file index.js \
261
+ --bundle-output build/sourcemaps/index.android.bundle --sourcemap-output build/sourcemaps/index.android.packager.map
262
+ node_modules/react-native/sdks/hermesc/osx-bin/hermesc -O -emit-binary -output-source-map \
263
+ -out=build/sourcemaps/index.android.bundle.hbc build/sourcemaps/index.android.bundle
264
+ node node_modules/react-native/scripts/compose-source-maps.js \
265
+ build/sourcemaps/index.android.packager.map build/sourcemaps/index.android.bundle.hbc.map \
266
+ -o build/sourcemaps/index.android.bundle.map
267
+
268
+ export GUIDELY_SOURCEMAP_KEY=gly_sk_… GUIDELY_API_URL=https://<your Guidely API>
269
+ npx @guidelytech/sourcemaps upload --dir build/sourcemaps --url-prefix / --release 2.4.1 --include .bundle.map
270
+ # iOS (main.jsbundle + main.jsbundle.map): --include .jsbundle.map
271
+ ```
272
+
273
+ On iOS, set `export SOURCEMAP_FILE="$DERIVED_FILE_DIR/main.jsbundle.map"` in the *Bundle React Native
274
+ code and images* build phase; React Native then writes the composed map next to the build, and the
275
+ bundle is `main.jsbundle` inside the `.app`. `--release` must equal the `release` prop (default: the
276
+ app version, `CFBundleShortVersionString` / `versionName`).
277
+
278
+ ### Native crashes
279
+
280
+ This SDK is pure JavaScript: it reports JavaScript errors only. Native crashes (signals,
281
+ `NSException`, JVM exceptions, ANRs) are reported by Guidely's native iOS and Android SDKs, which the
282
+ React Native SDK does not embed; capturing them in a React Native app means adding the native SDK
283
+ to the iOS/Android projects as well and is not set up by this package yet.
284
+
168
285
  ## Session replay
169
286
 
170
287
  Records sessions for the dashboard's **Replays** player: masked screenshots (about one a second