@allstak/react-native 0.3.0 → 0.3.2

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
@@ -1,223 +1,457 @@
1
1
  # @allstak/react-native
2
2
 
3
- **Native crash + JS error capture for React Native. iOS and Android auto-wired.**
3
+ AllStak's React Native SDK captures JS errors, render errors, unhandled promises, logs, HTTP breadcrumbs/events, navigation breadcrumbs, device tags, and source maps from React Native apps.
4
+
5
+ Use one wrapper, keep privacy-first defaults, support Hermes, and upload React Native source maps through build hooks instead of manual release chores.
4
6
 
5
7
  [![npm version](https://img.shields.io/npm/v/@allstak/react-native.svg)](https://www.npmjs.com/package/@allstak/react-native)
6
8
  [![CI](https://github.com/allstak-io/allstak-react-native/actions/workflows/ci.yml/badge.svg)](https://github.com/allstak-io/allstak-react-native/actions)
7
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
10
 
9
- Official AllStak SDK for React Native — hooks `ErrorUtils`, Hermes rejection tracking, and native crash capture on iOS and Android.
11
+ ## 1. Overview
10
12
 
11
- ## Dashboard
13
+ Use `@allstak/react-native` when you want production mobile error context without wiring several separate tools: a provider-first setup, automatic JS/runtime capture, privacy-safe breadcrumbs, Hermes-aware tags, and source-map upload through EAS, Gradle, Xcode, or custom CI build hooks.
12
14
 
13
- View captured events live at [app.allstak.sa](https://app.allstak.sa).
15
+ ## 2. Installation
14
16
 
15
- ![AllStak dashboard](https://app.allstak.sa/images/dashboard-preview.png)
17
+ ```bash
18
+ npm install @allstak/react-native
19
+ ```
16
20
 
17
- ## Features
21
+ Peer expectations:
18
22
 
19
- - `ErrorUtils.setGlobalHandler` integration for JS crash capture
20
- - Hermes unhandled promise rejection tracking
21
- - `Platform.OS` / `Platform.Version` auto-tags on every event
22
- - Native layers (Obj-C/Swift, Java/Kotlin) ship under `native/` for fatal crash capture
23
- - Breadcrumbs and user/tag context via the shared core API
24
- - Works with RN 0.70+
23
+ | Peer | Supported |
24
+ | --- | --- |
25
+ | React | `>=16.8.0` |
26
+ | React Native | `>=0.70` |
27
+ | Expo | Supported in Expo apps. Expo Go can use the JS SDK paths; native crash capture requires native modules in a dev client or native build. |
25
28
 
26
- ## What You Get
29
+ The published package is standalone: no browser DOM, Node runtime, or `@allstak/js` dependency is required.
27
30
 
28
- Once integrated, every event flows to your AllStak dashboard:
31
+ ## 3. Quick Start Provider-first
29
32
 
30
- - **JS errors** stack traces, component names, Hermes rejections
31
- - **Native crashes** — iOS (Obj-C/Swift) and Android (Java/Kotlin) fatals
32
- - **Logs** — structured logs with search and filters
33
- - **HTTP** — outbound request timing, status codes, failed calls
34
- - **Device tags** — `Platform.OS`, `Platform.Version`, release channel
35
- - **Alerts** — email and webhook notifications on regressions
33
+ Provider-first setup is the recommended path.
36
34
 
37
- ## Installation
35
+ ```tsx
36
+ import { AllStakProvider } from "@allstak/react-native";
38
37
 
39
- ```bash
40
- npm install @allstak/react-native
38
+ export default function App() {
39
+ return (
40
+ <AllStakProvider
41
+ apiKey="ask_live_..."
42
+ environment="production"
43
+ release="mobile@1.0.0+1"
44
+ dist="ios-hermes"
45
+ debug
46
+ >
47
+ <AppRoot />
48
+ </AllStakProvider>
49
+ );
50
+ }
41
51
  ```
42
52
 
43
- ## Quick Start
53
+ `AllStakProvider` automatically:
44
54
 
45
- > Create a project at [app.allstak.sa](https://app.allstak.sa) to get your API key.
55
+ - initializes the SDK
56
+ - installs React Native integrations
57
+ - wraps children with an error boundary
58
+ - captures render errors
59
+ - captures global JS errors through `ErrorUtils`
60
+ - captures unhandled promise rejections, including Hermes-native promise rejections
61
+ - attaches platform, device, architecture, Hermes, release, dist, and environment tags
62
+ - installs console, HTTP, XHR, fetch, and AppState breadcrumbs according to config
63
+ - attempts navigation auto-instrumentation when possible
46
64
 
47
- ```ts
48
- import { Platform } from 'react-native';
49
- import { AllStak, installReactNative } from '@allstak/react-native';
65
+ Navigation note: Metro/React Native static bundling currently prevents the SDK from guaranteeing automatic React Navigation patching in normal native Metro builds. The provider logs the status when `debug` is enabled. Use `instrumentReactNavigation(navigationRef)` when you need guaranteed navigation breadcrumbs.
50
66
 
51
- AllStak.init({
52
- apiKey: process.env.ALLSTAK_API_KEY!,
53
- environment: 'production',
54
- release: 'com.app@1.0.3+5',
55
- dist: Platform.OS, // 'ios' | 'android' — used as the dashboard filter for binary builds
56
- enableHttpTracking: true, // auto-instrument fetch + XHR + axios — see "HTTP tracking" below
57
- });
58
- installReactNative(); // ErrorUtils + Hermes promise rejections + Platform tags
67
+ ## 4. Verify It Works
68
+
69
+ Add a temporary test button or run these snippets after the provider has mounted:
70
+
71
+ ```tsx
72
+ import { AllStak } from "@allstak/react-native";
59
73
 
60
- AllStak.captureException(new Error('test: hello from allstak-react-native'));
74
+ AllStak.captureException(new Error("AllStak test error"));
75
+ AllStak.captureMessage("AllStak test log");
76
+ console.warn("AllStak warning breadcrumb");
77
+ Promise.reject(new Error("AllStak unhandled rejection test"));
61
78
  ```
62
79
 
63
- Run the app — the test error appears in your dashboard within seconds.
80
+ Expected result:
81
+
82
+ - with `debug`, Metro shows `[AllStak] Initialized — session <id>`
83
+ - the manual error appears in the AllStak dashboard
84
+ - the message appears as a log event
85
+ - the warning is attached as a breadcrumb to the next captured error
86
+ - the unhandled rejection is captured with `metadata.source = "unhandledRejection"`
87
+
88
+ ## 5. Automatic Capture Matrix
89
+
90
+ | Feature | Default | Config | Status | Notes |
91
+ | --- | --- | --- | --- | --- |
92
+ | Render errors | On with provider | `fallback`, `onError`; manual setup skips the boundary | Verified on iOS simulator | Captured with `metadata.source = "AllStakProvider.ErrorBoundary"` and component stack. |
93
+ | Global JS errors | On | `autoErrorHandler` | Verified on iOS simulator; unit-tested elsewhere | Uses `ErrorUtils.setGlobalHandler`. |
94
+ | Unhandled promises | On | `autoPromiseRejections` | Verified on iOS simulator | Uses Hermes rejection tracker plus polyfill/browser fallbacks. |
95
+ | Manual `captureException` | Manual | `AllStak.captureException(error)` | Verified on iOS simulator and backend contract tests | Posts to `/ingest/v1/errors`. |
96
+ | Manual `captureMessage` | Manual | `AllStak.captureMessage(message, level?)` | Verified on iOS simulator and backend contract tests | Info/warn go to logs; error/fatal also create error events. |
97
+ | `console.warn` / `console.error` | On | `autoConsoleBreadcrumbs`, `captureConsole` | Verified on iOS simulator | Breadcrumbs only; original console methods still run. |
98
+ | `console.log` / `console.info` | Off | `captureConsole={{ log: true, info: true }}` | Verified on iOS simulator | Opt-in only to avoid noisy dashboards. |
99
+ | Fetch breadcrumbs | On | `autoFetchBreadcrumbs` | Verified on iOS simulator | Query strings are stripped from breadcrumb URLs. |
100
+ | XHR breadcrumbs | On | `autoNetworkCapture` | Unit-tested; RN fetch is XHR-based in many setups | Skips AllStak ingest host to avoid recursion. |
101
+ | HTTP 4xx/5xx | On for breadcrumbs | `autoFetchBreadcrumbs`, `autoNetworkCapture` | Verified on iOS simulator | Error-level breadcrumb for status `>=400`. |
102
+ | Network failures | On for breadcrumbs | `autoFetchBreadcrumbs`, `autoNetworkCapture` | Verified on iOS simulator | Records failed request breadcrumb and rethrows original error. |
103
+ | Full HTTP request events | Off | `enableHttpTracking`, `httpTracking` | Verified on iOS simulator | Posts to `/ingest/v1/http_requests`; headers/bodies off by default. |
104
+ | AppState breadcrumbs | On | `autoAppStateBreadcrumbs` | Listener verified; OS transitions not fully scripted | Foreground/background transition testing is environment-dependent. |
105
+ | Navigation breadcrumbs | Best effort automatic, manual fallback guaranteed | `autoNavigationBreadcrumbs`, `instrumentReactNavigation(ref)` | Manual path verified on iOS simulator; auto path unit-tested under Node; Metro native auto-patch intentionally falls back | Use manual ref instrumentation for guaranteed React Navigation breadcrumbs. |
106
+ | Device/platform tags | On | `autoDeviceTags` | Verified on iOS simulator | Adds `device.os`, `device.osVersion`, `device.model` where available. |
107
+ | Hermes detection | On | automatic | Verified on iOS simulator | Adds `rn.hermes` and selects `ios-hermes` / `android-hermes` dist when possible. |
108
+ | Release/dist/environment tags | Environment defaults to `production`; release is caller-supplied; dist auto-detected unless overridden | `environment`, `release`, `dist` | Verified on iOS simulator | `release` and `dist` must match uploaded source maps for symbolication. |
109
+ | Native iOS crashes | Manual drain API; native module must be linked | `drainPendingNativeCrashes(release?)` | Verified on iOS simulator | Crash is stored by native handler and sent on next launch. Provider does not call this automatically. |
110
+ | Native Android crashes | Manual drain API; native module must be linked | `drainPendingNativeCrashes(release?)` | Implemented, not emulator/device verified | Android native module and dev crash trigger exist; emulator verification was blocked by disk in latest report. |
111
+ | Offline queue | On, in-memory only | transport internal | Backend-contract verified | Buffered retry works while process lives; events are lost after app restart. |
112
+ | Source maps | Build-time hook | EAS, Gradle, Xcode, or custom CI hooks | Build hooks implemented; backend upload path exists | Hooks inject `debugId` and upload maps. End-to-end Hermes symbolication still depends on matching release/dist/debugId. |
113
+
114
+ ## 6. Configuration Reference
115
+
116
+ `AllStakProvider` accepts the SDK config and React Native integration flags:
117
+
118
+ | Prop | Type | Default | Notes |
119
+ | --- | --- | --- | --- |
120
+ | `apiKey` | `string` | required | Runtime project API key such as `ask_live_...`. Not the source-map upload token. |
121
+ | `environment` | `string` | `"production"` | Attached to errors, logs, spans, and HTTP events. |
122
+ | `release` | `string` | unset | App release, for example `mobile@1.2.3+45`. Required for source-map matching. |
123
+ | `dist` | `string` | auto-detected | Override build flavor, for example `android-hermes`. |
124
+ | `debug` | `boolean` | `false` | Prints SDK init and selected integration status logs. |
125
+ | `fallback` | `ReactNode` or function | `null` on render error | Error boundary fallback UI. |
126
+ | `onError` | function | unset | Called after render error capture. |
127
+ | `destroyOnUnmount` | `boolean` | `false` | Keep `false` for app roots, Fast Refresh, and Strict Mode. |
128
+ | `captureConsole` | object | `{ warn: true, error: true, log: false, info: false }` | Per-method console breadcrumb flags. |
129
+ | `autoConsoleBreadcrumbs` | `boolean` | `true` | Kill switch for console wrapping. |
130
+ | `autoFetchBreadcrumbs` | `boolean` | `true` | Wraps `fetch` for HTTP breadcrumbs. |
131
+ | `autoNetworkCapture` | `boolean` | `true` | Wraps `XMLHttpRequest` for network breadcrumbs. |
132
+ | `enableHttpTracking` | `boolean` | `false` | Enables full HTTP request events. |
133
+ | `httpTracking` | `HttpTrackingOptions` | privacy-first defaults | Controls body/header capture, redaction, ignore lists, and byte limits. |
134
+ | `autoAppStateBreadcrumbs` | `boolean` | `true` | Captures AppState changes as breadcrumbs. |
135
+ | `autoDeviceTags` | `boolean` | `true` | Captures platform/device tags. |
136
+ | `autoNavigationBreadcrumbs` | `boolean` | `true` | Best-effort auto navigation patch; manual fallback is recommended for Metro-native builds. |
137
+
138
+ Common optional props also supported by the underlying client include `host`, `user`, `tags`, `sampleRate`, `beforeSend`, `tracesSampleRate`, `service`, and `replay`.
139
+
140
+ ```tsx
141
+ <AllStakProvider
142
+ apiKey="ask_live_..."
143
+ environment="production"
144
+ release="mobile@1.2.3+45"
145
+ dist="android-hermes"
146
+ captureConsole={{ log: true, info: true }}
147
+ enableHttpTracking
148
+ >
149
+ <AppRoot />
150
+ </AllStakProvider>
151
+ ```
64
152
 
65
- ## HTTP tracking
153
+ HTTP tracking defaults are intentionally conservative. Request bodies, response bodies, and headers are not captured unless explicitly enabled. Sensitive headers and query parameters are still redacted even when header/body capture is enabled.
66
154
 
67
- Setting `enableHttpTracking: true` (off by default) auto-wraps `fetch`,
68
- `XMLHttpRequest`, and `axios` (when the latter is installed) so every
69
- outbound HTTP call is recorded as an `http_request` event.
155
+ ## 7. Manual / Advanced API
70
156
 
71
- **Privacy defaults are aggressive — `enableHttpTracking: true` is safe
72
- to ship to production:**
157
+ ```ts
158
+ import {
159
+ AllStak,
160
+ instrumentReactNavigation,
161
+ drainPendingNativeCrashes,
162
+ } from "@allstak/react-native";
163
+
164
+ AllStak.captureException(error);
165
+ AllStak.captureMessage("message");
166
+ AllStak.addBreadcrumb("ui", "Tapped checkout", "info", { screen: "Checkout" });
167
+ AllStak.setUser({ id: "user_123", email: "user@example.com" });
168
+ AllStak.setTag("key", "value");
169
+ AllStak.setContext("device", { model: "simulator" });
170
+ await AllStak.flush();
171
+ AllStak.destroy();
172
+ ```
73
173
 
74
- - request bodies are **not** captured
75
- - response bodies are **not** captured
76
- - headers are **not** captured
77
- - `Authorization`, `Cookie`, `Set-Cookie`, `X-API-Key`, `X-Auth-Token`,
78
- `Proxy-Authorization` are **always** redacted
79
- - query params named `token`, `password`, `api_key`, `apikey`,
80
- `authorization`, `auth`, `secret`, `access_token`, `refresh_token`,
81
- `session`, `sessionid`, `jwt` are **always** redacted in the URL
82
- - own-ingest URLs (your AllStak host) are skipped to avoid recursion
174
+ Other advanced APIs include `setTags`, `setExtra`, `setExtras`, `setLevel`, `setFingerprint`, `withScope`, tracing helpers, `getReplay`, and manual `instrumentAxios`.
83
175
 
84
- Enable richer capture only on routes you control:
176
+ Manual setup is available when you need full initialization control:
85
177
 
86
178
  ```ts
179
+ import { AllStak, installReactNative } from "@allstak/react-native";
180
+
87
181
  AllStak.init({
88
- apiKey: '...',
89
- enableHttpTracking: true,
90
- httpTracking: {
91
- captureRequestBody: true, // off by default
92
- captureResponseBody: true, // off by default
93
- captureHeaders: true, // off by default — auth headers still hard-redacted
94
- redactHeaders: ['x-tenant'], // additional names on top of the always-redact list
95
- redactQueryParams: ['custom_id'],
96
- ignoredUrls: [/health/i, '/metrics'],
97
- allowedUrls: [], // if non-empty, ONLY these URLs are captured
98
- maxBodyBytes: 4096, // bodies truncated past this with `…[truncated]`
99
- },
182
+ apiKey: "ask_live_...",
183
+ environment: "production",
184
+ release: "mobile@1.2.3+45",
100
185
  });
186
+
187
+ installReactNative();
101
188
  ```
102
189
 
103
- ### axios
190
+ Manual navigation fallback:
104
191
 
105
- If the project uses axios with a non-XHR adapter (rare on RN), explicitly
106
- instrument the instance — idempotent, so safe to call twice:
192
+ ```tsx
193
+ import { NavigationContainer, useNavigationContainerRef } from "@react-navigation/native";
194
+ import { instrumentReactNavigation } from "@allstak/react-native";
107
195
 
108
- ```ts
109
- import axios from 'axios';
110
- const api = AllStak.instrumentAxios(axios.create({ baseURL: 'https://api.example.com' }));
196
+ export function AppNavigation() {
197
+ const navigationRef = useNavigationContainerRef();
198
+
199
+ return (
200
+ <NavigationContainer
201
+ ref={navigationRef}
202
+ onReady={() => instrumentReactNavigation(navigationRef)}
203
+ >
204
+ {/* navigators */}
205
+ </NavigationContainer>
206
+ );
207
+ }
111
208
  ```
112
209
 
113
- ### Errors auto-link to recent failed requests
210
+ Auto navigation works only where the SDK can safely patch `@react-navigation/native`. In normal Metro-native builds, manual ref instrumentation is the guaranteed path.
211
+
212
+ ## 8. Source Maps — automatic via build hooks
213
+
214
+ React Native source maps become hands-off after adding one build hook for your build system. The hook injects a `debugId` into both the JS bundle and source map, then uploads the map to AllStak.
215
+
216
+ ### EAS / Expo
114
217
 
115
- When `enableHttpTracking: true` is on, the most recent failed HTTP
116
- requests (status >= 400 or network error, last 10) are automatically
117
- attached to the next `captureException` under
118
- `metadata['http.recentFailed']`. Bodies are NOT included in this
119
- snapshot unless body capture is enabled — only `method`, `url`,
120
- `statusCode`, `durationMs`, `error`.
218
+ Add the AllStak config plugin to your `app.json` (or `app.config.js`):
121
219
 
122
- ### `dist`
220
+ ```json
221
+ {
222
+ "expo": {
223
+ "plugins": ["@allstak/react-native"]
224
+ }
225
+ }
226
+ ```
123
227
 
124
- `dist: Platform.OS` is the recommended pattern — it labels every event
125
- with the binary build (`ios` / `android`) so the dashboard can filter
126
- to a single platform when triaging.
228
+ Then add the EAS post-build hook:
127
229
 
128
- ## Get Your API Key
230
+ ```json
231
+ {
232
+ "scripts": {
233
+ "eas-build-on-success": "node ./node_modules/@allstak/react-native/build-hooks/eas-post-bundle.js"
234
+ }
235
+ }
236
+ ```
129
237
 
130
- 1. Sign up at [app.allstak.sa](https://app.allstak.sa)
131
- 2. Create a project
132
- 3. Copy your API key from **Project Settings → API Keys**
133
- 4. Export it as `ALLSTAK_API_KEY` or pass it to `installReactNative(...)`
238
+ Required environment:
134
239
 
135
- ## Configuration
240
+ - `ALLSTAK_UPLOAD_TOKEN`
241
+ - `ALLSTAK_RELEASE`
242
+ - `ALLSTAK_API_URL` optional, defaults to AllStak production API
136
243
 
137
- | Option | Type | Required | Default | Description |
138
- |---|---|---|---|---|
139
- | `apiKey` | `string` | yes | — | Project API key (`ask_live_…`) |
140
- | `environment` | `string` | no | — | Deployment env |
141
- | `release` | `string` | no | — | App version |
142
- | `host` | `string` | no | `https://api.allstak.sa` | Ingest host override |
143
- | `user` | `{ id?, email? }` | no | — | Default user context |
144
- | `tags` | `Record<string,string>` | no | — | Default tags |
244
+ `ALLSTAK_UPLOAD_TOKEN` is a project-scoped upload token from Project Settings. It is not the runtime `apiKey`.
145
245
 
146
- ## Example Usage
246
+ ### Android Gradle
147
247
 
148
- Capture a caught exception:
248
+ Add this to `android/app/build.gradle` after the React Native Gradle plugin setup:
149
249
 
150
- ```ts
151
- try {
152
- await api.fetchFeed();
153
- } catch (e) {
154
- AllStak.captureException(e as Error, { screen: 'Feed' });
155
- }
250
+ ```groovy
251
+ apply from: "../../node_modules/@allstak/react-native/build-hooks/allstak-sourcemaps.gradle"
156
252
  ```
157
253
 
158
- Send a log from a screen:
254
+ Supported Gradle properties / environment:
159
255
 
160
- ```ts
161
- AllStak.captureMessage('User opened Settings', 'info');
162
- ```
256
+ - `ALLSTAK_UPLOAD_TOKEN` or `allstakUploadToken`
257
+ - `ALLSTAK_RELEASE` or `allstakRelease`
258
+ - optional API URL override when self-hosting
163
259
 
164
- Tag the current build channel:
260
+ Then run your normal release task, for example:
165
261
 
166
- ```ts
167
- AllStak.setTag('release-channel', 'beta');
168
- AllStak.setUser({ id: userId });
262
+ ```bash
263
+ cd android
264
+ ./gradlew :app:bundleRelease
169
265
  ```
170
266
 
171
- ## Production Endpoint
267
+ ### iOS Xcode Build Phase
172
268
 
173
- Production endpoint: `https://api.allstak.sa`. Override via `host` for self-hosted installs:
269
+ Add a Run Script build phase after "Bundle React Native code and images":
174
270
 
175
- ```ts
176
- installReactNative({ apiKey: '...', host: 'https://allstak.mycorp.com' });
271
+ ```sh
272
+ "${SRCROOT}/../node_modules/@allstak/react-native/build-hooks/xcode-build-phase.sh"
177
273
  ```
178
274
 
179
- ## Source maps (Hermes / Metro)
275
+ Set these in the build phase, `.xcconfig`, or CI shell:
180
276
 
181
- JS bundles produced by Metro (with or without Hermes) are minified in
182
- release builds — without source-map upload, your dashboard stacks will
183
- read like `at e (index.bundle:1:42031)`. Wire it up once during release
184
- build via `@allstak/js/sourcemaps` (devDependency only):
277
+ ```sh
278
+ ALLSTAK_UPLOAD_TOKEN=aspk_...
279
+ ALLSTAK_RELEASE=mobile@1.2.3+45
280
+ ```
281
+
282
+ The script is designed for release builds; debug builds should not upload source maps.
283
+
284
+ ### Custom CI
185
285
 
186
286
  ```bash
187
- npm install -D @allstak/js
287
+ node ./node_modules/@allstak/react-native/build-hooks/upload-sourcemaps.js \
288
+ --platform ios \
289
+ --bundle path/to/main.jsbundle \
290
+ --sourcemap path/to/main.jsbundle.map \
291
+ --release mobile@1.2.3+45 \
292
+ --dist ios-hermes
188
293
  ```
189
294
 
190
- ```sh
191
- # Build the release bundle as you normally would
192
- npx react-native bundle \
193
- --platform android --dev false --entry-file index.js \
194
- --bundle-output android-release.bundle \
195
- --sourcemap-output android-release.bundle.map
295
+ Programmatic API:
196
296
 
197
- # Hermes-compile (if Hermes is enabled — the default on RN 0.70+)
198
- hermes-compiler --emit-binary --output-source-map \
199
- -out android-release.hbc android-release.bundle
297
+ ```js
298
+ const { uploadReactNativeSourcemap } = require("@allstak/react-native/sourcemaps");
200
299
 
201
- # Upload to AllStak — debugId injection + map upload
202
- node -e "import('@allstak/js/sourcemaps').then(({ processBuildOutput }) => \
203
- processBuildOutput({ dir: '.', release: process.env.RELEASE, \
204
- token: process.env.ALLSTAK_UPLOAD_TOKEN }))"
300
+ await uploadReactNativeSourcemap({
301
+ platform: "ios",
302
+ bundle: "path/to/main.jsbundle",
303
+ sourcemap: "path/to/main.jsbundle.map",
304
+ release: "mobile@1.2.3+45",
305
+ dist: "ios-hermes",
306
+ token: process.env.ALLSTAK_UPLOAD_TOKEN,
307
+ });
205
308
  ```
206
309
 
207
- For iOS (`react-native bundle --platform ios`) the same flow applies.
208
- Add this as a release-only step in your CI; the runtime SDK reads the
209
- `debugId` from each frame and resolves the matching map server-side.
310
+ How matching works:
210
311
 
211
- > **Status:** the upload pipeline is the same one used by the web SDK
212
- > and is unit-tested in `@allstak/js/tests/sourcemaps-*.test.ts`. Native
213
- > Hermes-bytecode mapping has not yet been integration-tested end-to-end
214
- > against the AllStak symbolicator on a real device build flag any
215
- > off-by-one source maps in [issues](https://github.com/AllStak/allstak-react-native/issues).
312
+ - `debugId` is injected into the bundle and the source map.
313
+ - The backend matches stack frames using `debugId`, `release`, and `dist`.
314
+ - Maps are uploaded to `/api/v1/artifacts/upload`.
315
+ - Upload auth uses `X-AllStak-Upload-Token`; do not use the runtime `ask_live_...` API key.
316
+
317
+ If no upload token is present, the hook can run in inject-only mode so CI can still produce bundles with debug IDs without uploading artifacts.
318
+
319
+ ## 9. Native Crashes
320
+
321
+ JS errors are verified. Native crash files exist for iOS and Android, and the SDK exports a drain API:
322
+
323
+ ```ts
324
+ import { drainPendingNativeCrashes } from "@allstak/react-native";
325
+
326
+ await drainPendingNativeCrashes("mobile@1.2.3+45");
327
+ ```
328
+
329
+ The provider does not call `drainPendingNativeCrashes()` automatically. Call it once early after SDK initialization, before user flows start:
330
+
331
+ ```tsx
332
+ import { AllStakProvider, drainPendingNativeCrashes } from "@allstak/react-native";
333
+ import { useEffect } from "react";
334
+
335
+ function AppRoot() {
336
+ useEffect(() => {
337
+ drainPendingNativeCrashes("mobile@1.2.3+45");
338
+ }, []);
339
+
340
+ return <Routes />;
341
+ }
342
+ ```
343
+
344
+ Current verification status:
345
+
346
+ - iOS native crash capture and drain were verified end-to-end on an iOS simulator.
347
+ - Android native crash capture code exists and has parity with iOS, but emulator/device verification was not completed in the latest report.
348
+ - Do not mark Android native crash capture production-ready until an Android emulator or device run proves crash -> relaunch -> drain -> backend ingest.
349
+
350
+ Native modules are not available in Expo Go. Use a dev client or native build for native crash verification.
351
+
352
+ ## 10. Privacy Defaults
353
+
354
+ - Headers are off by default.
355
+ - Request bodies are off by default.
356
+ - Response bodies are off by default.
357
+ - `Authorization`, `Cookie`, `Set-Cookie`, `X-API-Key`, `X-Auth-Token`, and `Proxy-Authorization` are always redacted.
358
+ - Sensitive query parameters such as `token`, `password`, `api_key`, `authorization`, `secret`, `access_token`, `refresh_token`, and `jwt` are redacted.
359
+ - `console.log` and `console.info` are off by default.
360
+ - `console.warn` and `console.error` are captured as breadcrumbs by default.
361
+ - Source maps containing `sourcesContent` may be rejected when organization privacy mode requires stripped sources. Use `stripSources: true` or configure your build hook accordingly.
362
+
363
+ ## 11. Troubleshooting
364
+
365
+ ### Events not appearing
366
+
367
+ - Confirm `apiKey` is the runtime project key and starts with `ask_live_...`.
368
+ - Enable `debug` and check for `[AllStak] Initialized — session <id>`.
369
+ - Confirm the app can reach `https://api.allstak.sa`.
370
+ - For self-hosted installs, pass `host="https://your-api.example.com"`.
371
+ - Check the project/environment you are viewing in the dashboard.
372
+
373
+ ### Wrong `apiKey`
374
+
375
+ Runtime ingestion uses the project API key. Source-map upload uses `ALLSTAK_UPLOAD_TOKEN`. They are different credentials and are not interchangeable.
376
+
377
+ ### Upload token vs runtime API key confusion
378
+
379
+ - `apiKey="ask_live_..."`: ships runtime errors/logs/breadcrumbs.
380
+ - `ALLSTAK_UPLOAD_TOKEN=aspk_...`: uploads source maps and artifacts in CI.
381
+ - Never put the upload token in app code.
382
+
383
+ ### Source maps uploaded but stack is not symbolicated
384
+
385
+ - Ensure the runtime `release` matches `ALLSTAK_RELEASE`.
386
+ - Ensure runtime `dist` matches the uploaded map's `dist`.
387
+ - Confirm the event contains a matching `debugId` or release/dist combination.
388
+ - Confirm the map was uploaded to the same project as the runtime API key.
389
+
390
+ ### Release/dist mismatch
391
+
392
+ Use stable release names such as `mobile@1.2.3+45` and platform dists such as `ios-hermes`, `android-hermes`, `ios-jsc`, or `android-jsc`. Mismatched values prevent backend joining between event and source map.
393
+
394
+ ### Hermes stacks not resolving
395
+
396
+ Hermes stack traces need the correct Metro/Hermes source-map chain. Use the provided build hooks instead of uploading a random map file from an intermediate build step.
397
+
398
+ ### Navigation breadcrumbs not appearing
399
+
400
+ - In Metro-native builds, use `instrumentReactNavigation(navigationRef)`.
401
+ - Confirm `NavigationContainer` has mounted and `onReady` fires.
402
+ - Avoid deep imports that bypass `@react-navigation/native`.
403
+ - Enable `debug` to see whether auto navigation instrumentation was applied or skipped.
404
+
405
+ ### Metro / Expo issues
406
+
407
+ - Rebuild after adding native modules or source-map hooks.
408
+ - Expo Go cannot load custom native crash modules.
409
+ - For native crash testing, use an Expo dev client, simulator build, or physical device build.
410
+
411
+ ### Android Gradle hook not running
412
+
413
+ - Confirm the `apply from` path is relative to `android/app/build.gradle`.
414
+ - Confirm `ALLSTAK_RELEASE` and `ALLSTAK_UPLOAD_TOKEN` are visible to Gradle.
415
+ - Run with `--info` to verify the AllStak source-map task executes.
416
+
417
+ ### Xcode build phase path wrong
418
+
419
+ - The recommended path is `"${SRCROOT}/../node_modules/@allstak/react-native/build-hooks/xcode-build-phase.sh"`.
420
+ - Place the phase after React Native bundles JS.
421
+ - Confirm the script has executable permissions after install.
422
+
423
+ ### AppState breadcrumbs not easy to test
424
+
425
+ The listener registers at startup, but actual foreground/background transitions depend on simulator/device behavior. Verify by backgrounding and foregrounding the app, then capturing an error so breadcrumbs attach.
426
+
427
+ ### Docker/backend local issue is not an SDK issue
428
+
429
+ If local backend testing returns `401 INVALID_API_KEY` or nothing reaches ClickHouse, verify the API key exists in the same database used by the running backend. The latest backend verification report found that using the wrong local Postgres instance caused false SDK failures.
430
+
431
+ ## 12. Verification Status
432
+
433
+ Latest verification reports are in `docs/reports/`:
434
+
435
+ - `react-native-provider-verification.md`
436
+ - `react-native-backend-ingestion-verification.md`
437
+ - `react-native-production-readiness.md`
438
+ - `console-and-navigation-auto.md`
439
+
440
+ Current status from those reports:
441
+
442
+ | Area | Status |
443
+ | --- | --- |
444
+ | Unit tests | Latest production-readiness report: 132 passing total, including 126 unit tests and 6 live backend-contract tests. Earlier provider report recorded 98/98 before later additions. |
445
+ | iOS simulator verified paths | Provider init, render errors, global JS errors, unhandled promises, manual exception/message, console warn/error defaults, console log/info opt-in behavior, fetch breadcrumbs, HTTP 4xx/5xx/network failure, full HTTP request events, device/platform/Hermes/dist tags, manual React Navigation breadcrumbs, iOS native crash drain. |
446
+ | Android emulator verified paths | Not completed in latest production-readiness pass; emulator boot was blocked by disk. Android native crash and JS paths remain implemented but not device-verified. |
447
+ | Backend ingestion verified paths | `/ingest/v1/errors`, `/ingest/v1/logs`, `/ingest/v1/http_requests`; 8 canonical curl payload shapes accepted; live SDK events landed in ClickHouse; backend-contract tests cover buffering and invalid-key resilience. |
448
+ | Source-map backend status | `/api/v1/artifacts/upload` upload path exists and build hooks inject/upload debug-ID source maps. Full Hermes symbolication remains dependent on matching release/dist/debugId and should be validated per app release pipeline. |
449
+ | Remaining gaps | Android device/emulator verification, persistent offline queue, full AppState transition verification, and per-project source-map symbolication verification in release CI. |
216
450
 
217
451
  ## Links
218
452
 
219
- - Documentation: https://docs.allstak.sa
220
453
  - Dashboard: https://app.allstak.sa
454
+ - Documentation: https://docs.allstak.sa
221
455
  - Source: https://github.com/allstak-io/allstak-react-native
222
456
 
223
457
  ## License