@allstak/react-native 0.3.2 → 0.3.3

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,459 +1,156 @@
1
1
  # @allstak/react-native
2
2
 
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.
3
+ AllStak React Native SDK for Expo and React Native CLI apps. It captures JavaScript errors, render errors, unhandled promises where available, breadcrumbs, HTTP metadata, release/environment/device tags, user context, and native crashes when native modules are linked in a dev client or native build.
4
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.
5
+ Stability: beta. Live dashboard certification and native crash proof are not claimed until verified with real credentials and a native build.
6
6
 
7
- [![npm version](https://img.shields.io/npm/v/@allstak/react-native.svg)](https://www.npmjs.com/package/@allstak/react-native)
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)
9
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ ## 1. Automatic Setup With Wizard
10
8
 
11
- ## 1. Overview
12
-
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.
14
-
15
- ## 2. Installation
9
+ Recommended flow:
16
10
 
17
11
  ```bash
18
- npm install @allstak/react-native
12
+ npx @allstak/wizard setup --integration react-native
19
13
  ```
20
14
 
21
- Peer expectations:
22
-
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. |
15
+ For Expo managed projects, this also patches static `app.json` plugin config. The only value you may need to enter is the AllStak ingest API key.
28
16
 
29
- The published package is standalone: no browser DOM, Node runtime, or `@allstak/js` dependency is required.
17
+ ## 2. What The Wizard Changes
30
18
 
31
- ## 3. Quick Start — Provider-first
19
+ The wizard:
32
20
 
33
- Provider-first setup is the recommended path.
21
+ - Installs `@allstak/react-native`.
22
+ - Detects Expo managed, Expo bare/prebuilt, and React Native CLI projects.
23
+ - Writes managed `ALLSTAK_*` and `EXPO_PUBLIC_ALLSTAK_*` env vars.
24
+ - Detects `App.tsx`, `App.jsx`, `App.ts`, `App.js`, `src/App.*`, or root `index.*`.
25
+ - Wraps the app root with `AllStakProvider`.
26
+ - Preserves existing providers and app content.
27
+ - Adds package scripts for verification and source-map upload.
28
+ - Adds `@allstak/react-native` to `app.json expo.plugins[]` when a static Expo config exists.
29
+ - Leaves native linking to React Native autolinking; no manual linking is required for standard RN CLI/dev-client builds.
30
+ - Supports dry-run, repair, idempotent re-runs, and uninstall.
34
31
 
35
- ```tsx
36
- import { AllStakProvider } from "@allstak/react-native";
37
-
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
- }
51
- ```
32
+ ## 3. Verification
52
33
 
53
- `AllStakProvider` automatically:
34
+ After setup:
54
35
 
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
64
-
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.
66
-
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";
73
-
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"));
36
+ ```bash
37
+ npm run allstak:verify
38
+ npx @allstak/wizard doctor --integration react-native
78
39
  ```
79
40
 
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`.
41
+ For Expo:
139
42
 
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>
43
+ ```bash
44
+ npx expo config --type public
151
45
  ```
152
46
 
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.
47
+ For RN CLI, run your normal TypeScript/build checks. Dashboard delivery requires real credentials and must be verified in AllStak.
154
48
 
155
- ## 7. Manual / Advanced API
49
+ ## 4. Rollback / Uninstall
156
50
 
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();
51
+ ```bash
52
+ npx @allstak/wizard uninstall --integration react-native
172
53
  ```
173
54
 
174
- Other advanced APIs include `setTags`, `setExtra`, `setExtras`, `setLevel`, `setFingerprint`, `withScope`, tracing helpers, `getReplay`, and manual `instrumentAxios`.
175
-
176
- Manual setup is available when you need full initialization control:
55
+ Uninstall removes wizard-managed env blocks, package scripts, Expo plugin entries, imports, and provider wrappers. User-owned code outside wizard-managed setup is preserved.
177
56
 
178
- ```ts
179
- import { AllStak, installReactNative } from "@allstak/react-native";
57
+ ## 5. Manual Setup Fallback
180
58
 
181
- AllStak.init({
182
- apiKey: "ask_live_...",
183
- environment: "production",
184
- release: "mobile@1.2.3+45",
185
- });
59
+ Use manual setup only when the wizard cannot safely patch a custom root:
186
60
 
187
- installReactNative();
61
+ ```bash
62
+ npm install @allstak/react-native
188
63
  ```
189
64
 
190
- Manual navigation fallback:
191
-
192
65
  ```tsx
193
- import { NavigationContainer, useNavigationContainerRef } from "@react-navigation/native";
194
- import { instrumentReactNavigation } from "@allstak/react-native";
195
-
196
- export function AppNavigation() {
197
- const navigationRef = useNavigationContainerRef();
66
+ import { AllStakProvider } from '@allstak/react-native';
198
67
 
68
+ export default function App() {
199
69
  return (
200
- <NavigationContainer
201
- ref={navigationRef}
202
- onReady={() => instrumentReactNavigation(navigationRef)}
70
+ <AllStakProvider
71
+ apiKey={process.env.EXPO_PUBLIC_ALLSTAK_API_KEY ?? process.env.ALLSTAK_API_KEY}
72
+ host={process.env.EXPO_PUBLIC_ALLSTAK_HOST ?? process.env.ALLSTAK_HOST}
73
+ environment={process.env.EXPO_PUBLIC_ALLSTAK_ENVIRONMENT ?? 'production'}
74
+ release={process.env.EXPO_PUBLIC_ALLSTAK_RELEASE ?? process.env.ALLSTAK_RELEASE}
75
+ enableHttpTracking
203
76
  >
204
- {/* navigators */}
205
- </NavigationContainer>
77
+ {/* app */}
78
+ </AllStakProvider>
206
79
  );
207
80
  }
208
81
  ```
209
82
 
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
217
-
218
- Add the AllStak config plugin to your `app.json` (or `app.config.js`):
219
-
220
- ```json
221
- {
222
- "expo": {
223
- "plugins": ["@allstak/react-native"]
224
- }
225
- }
226
- ```
83
+ Manual capture:
227
84
 
228
- Then add the EAS post-build hook:
85
+ ```ts
86
+ import { AllStak } from '@allstak/react-native';
229
87
 
230
- ```json
231
- {
232
- "scripts": {
233
- "eas-build-on-success": "node ./node_modules/@allstak/react-native/build-hooks/eas-post-bundle.js"
234
- }
235
- }
88
+ AllStak.captureException(new Error('mobile failure'));
89
+ AllStak.captureMessage('checkout opened');
90
+ AllStak.setUser({ id: 'user_123' });
91
+ AllStak.addBreadcrumb({ type: 'navigation', message: 'Checkout' });
236
92
  ```
237
93
 
238
- Required environment:
94
+ ## 6. Configuration
239
95
 
240
- - `ALLSTAK_UPLOAD_TOKEN`
241
- - `ALLSTAK_RELEASE`
242
- - `ALLSTAK_API_URL` optional, defaults to AllStak production API
96
+ Provider props include:
243
97
 
244
- `ALLSTAK_UPLOAD_TOKEN` is a project-scoped upload token from Project Settings. It is not the runtime `apiKey`.
98
+ | Option | Default | Notes |
99
+ | --- | --- | --- |
100
+ | `apiKey` | required | Public mobile ingest key. |
101
+ | `host` | `https://api.allstak.sa` | Override for self-hosted ingest. |
102
+ | `environment` | `production` | Release environment tag. |
103
+ | `release` | unset | App version or build number. |
104
+ | `debug` | `false` | Enables SDK diagnostic logs. |
105
+ | `enableHttpTracking` | `false` | Captures HTTP metadata with redaction. |
106
+ | `autoCaptureJsErrors` | `true` | Captures ErrorUtils/global JS errors where available. |
107
+ | `autoUnhandledRejections` | `true` | Captures promise rejections where supported. |
108
+ | `captureConsole` | warn/error on | `log`/`info` stay off by default. |
109
+ | `beforeSend` | unset | Last-chance scrub/drop hook. |
245
110
 
246
- ### Android Gradle
111
+ ## 7. Privacy / PII / Redaction
247
112
 
248
- Add this to `android/app/build.gradle` after the React Native Gradle plugin setup:
113
+ Defaults are privacy-first:
249
114
 
250
- ```groovy
251
- apply from: "../../node_modules/@allstak/react-native/build-hooks/allstak-sourcemaps.gradle"
252
- ```
115
+ - Authorization, cookie, API key, token, and secret headers are always redacted.
116
+ - Sensitive query parameters are redacted.
117
+ - Request/response body capture is disabled unless explicitly enabled.
118
+ - Recursive JSON body redaction is available when body capture is enabled.
119
+ - Queue size is capped and transport failures fail open.
120
+ - Use `beforeSend` for app-specific PII removal.
253
121
 
254
- Supported Gradle properties / environment:
122
+ Do not send passwords, payment data, national IDs, raw tokens, or raw request/response bodies unless you have verified redaction in your app.
255
123
 
256
- - `ALLSTAK_UPLOAD_TOKEN` or `allstakUploadToken`
257
- - `ALLSTAK_RELEASE` or `allstakRelease`
258
- - optional API URL override when self-hosting
124
+ ## 8. Source Maps / Releases
259
125
 
260
- Then run your normal release task, for example:
126
+ The wizard adds:
261
127
 
262
128
  ```bash
263
- cd android
264
- ./gradlew :app:bundleRelease
129
+ npm run allstak:sourcemaps
265
130
  ```
266
131
 
267
- ### iOS Xcode Build Phase
268
-
269
- Add a Run Script build phase after "Bundle React Native code and images":
270
-
271
- ```sh
272
- "${SRCROOT}/../node_modules/@allstak/react-native/build-hooks/xcode-build-phase.sh"
273
- ```
274
-
275
- Set these in the build phase, `.xcconfig`, or CI shell:
276
-
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
132
+ Set the same release in your app and source-map upload:
285
133
 
286
134
  ```bash
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
293
- ```
294
-
295
- Programmatic API:
296
-
297
- ```js
298
- const { uploadReactNativeSourcemap } = require("@allstak/react-native/sourcemaps");
299
-
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
- });
308
- ```
309
-
310
- How matching works:
311
-
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");
135
+ export ALLSTAK_RELEASE=my-app@1.2.3
136
+ export ALLSTAK_SOURCEMAP_TOKEN=...
137
+ npm run allstak:sourcemaps
327
138
  ```
328
139
 
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. |
140
+ Expo EAS users can call the package build hook from their EAS workflow. RN CLI users can call the upload hook after Metro/Hermes source maps are generated.
450
141
 
451
- ## Links
142
+ ## 9. Troubleshooting
452
143
 
453
- - Dashboard: https://app.allstak.sa
454
- - Documentation: https://docs.allstak.sa
455
- - Source: https://github.com/allstak-io/allstak-react-native
144
+ - No events: verify `ALLSTAK_API_KEY` or `EXPO_PUBLIC_ALLSTAK_API_KEY` and confirm one provider wrapper.
145
+ - Duplicate events: run `npx @allstak/wizard doctor --integration react-native`.
146
+ - Expo Go native crashes missing: expected. Native modules require a dev client or native build.
147
+ - Source maps missing: confirm `ALLSTAK_SOURCEMAP_TOKEN` and matching `release`.
148
+ - Build fails after setup: run uninstall, then rerun setup with `--dry-run` and inspect the planned diff.
456
149
 
457
- ## License
150
+ ## 10. Limitations
458
151
 
459
- MIT © AllStak
152
+ - Native crash capture is not available in Expo Go.
153
+ - Native crash proof requires a native build and dashboard verification.
154
+ - Live dashboard delivery is not proven by local tests.
155
+ - Some React Navigation breadcrumb paths may still need explicit app navigation lifecycle validation.
156
+ - Stable production launch requires live certification against your dashboard.
package/app.plugin.js CHANGED
@@ -3,4 +3,6 @@
3
3
  // app.json and Expo's `withPlugins` resolver finds it without an extra
4
4
  // import path. Expo looks for `app.plugin.js` at the package root by
5
5
  // convention.
6
- module.exports = require('./dist/expo-plugin.js');
6
+ const plugin = require('./dist/expo-plugin.js');
7
+
8
+ module.exports = plugin.default || plugin;
@@ -32,13 +32,9 @@ function withAllStak(config, options = {}) {
32
32
  release: options.release ?? existing.release,
33
33
  environment: options.environment ?? existing.environment,
34
34
  dist: options.dist ?? existing.dist,
35
- pluginVersion: "0.3.1"
35
+ pluginVersion: "0.3.3"
36
36
  };
37
37
  return next;
38
38
  }
39
39
  var expo_plugin_default = withAllStak;
40
- if (typeof module !== "undefined" && module.exports) {
41
- module.exports = withAllStak;
42
- module.exports.default = withAllStak;
43
- }
44
40
  //# sourceMappingURL=expo-plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/expo-plugin.ts"],"sourcesContent":["/**\n * Expo config plugin.\n *\n * Apply via `app.json`:\n *\n * {\n * \"expo\": {\n * \"plugins\": [\n * [\"@allstak/react-native\", { \"release\": \"mobile@1.2.3\", \"environment\": \"production\" }]\n * ]\n * }\n * }\n *\n * The plugin runs at `expo prebuild` / EAS build time and:\n *\n * 1. Adds the AllStak iOS pod (declared in `react-native.config.js`).\n * Expo's autolinking already picks this up — we just record metadata\n * so `expo doctor` can verify the install is wired.\n * 2. Stamps the chosen `release` into `expo-constants` extras so the JS\n * layer can read it at runtime via `Constants.expoConfig.extra._allstak`\n * without the host app needing to plumb it through env vars.\n * 3. Records that `@allstak/react-native` was loaded as an Expo plugin\n * for diagnostics.\n *\n * Pure config mutation — no native code is patched here. The native iOS\n * + Android modules under ./native are linked by Expo's existing\n * autolinking flow (which honors the `react-native.config.js` manifest).\n */\n\nexport interface AllStakExpoOptions {\n /** Release identifier (e.g. `mobile@1.2.3`). Stamped into `Constants.expoConfig.extra._allstak.release`. */\n release?: string;\n /** Environment label — `production`, `staging`, etc. */\n environment?: string;\n /** Optional dist tag (e.g. `ios-hermes`). Auto-detected at runtime if unset. */\n dist?: string;\n}\n\ninterface ExpoConfig {\n name?: string;\n extra?: Record<string, unknown>;\n plugins?: any[];\n [key: string]: any;\n}\n\ninterface ExpoConfigContext {\n modResults?: any;\n modRawConfig?: ExpoConfig;\n [key: string]: any;\n}\n\n/**\n * The plugin function itself. Expo's plugin runner calls it as\n * `(config, options) => modifiedConfig`. We avoid importing\n * `@expo/config-plugins` so the package has zero hard dependencies on the\n * Expo toolchain — the type checking happens at usage site instead.\n */\nfunction withAllStak(config: ExpoConfig & ExpoConfigContext, options: AllStakExpoOptions = {}): ExpoConfig {\n const next: ExpoConfig = { ...config };\n next.extra = { ...(config.extra ?? {}) };\n\n // Embed runtime-readable metadata under a namespaced key so we never\n // collide with the host app's other extras.\n const existing = (next.extra as any)._allstak ?? {};\n (next.extra as any)._allstak = {\n ...existing,\n release: options.release ?? existing.release,\n environment: options.environment ?? existing.environment,\n dist: options.dist ?? existing.dist,\n pluginVersion: '0.3.1',\n };\n\n return next;\n}\n\nexport default withAllStak;\n\n// Expose as a CommonJS function so `app.plugin.js`'s `require('./dist/expo-plugin.js')`\n// returns the plugin directly. The `declare const module` keeps the TS\n// type-checker happy without dragging in @types/node.\ndeclare const module: { exports: any };\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = withAllStak;\n module.exports.default = withAllStak;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAyDA,SAAS,YAAY,QAAwC,UAA8B,CAAC,GAAe;AACzG,QAAM,OAAmB,EAAE,GAAG,OAAO;AACrC,OAAK,QAAQ,EAAE,GAAI,OAAO,SAAS,CAAC,EAAG;AAIvC,QAAM,WAAY,KAAK,MAAc,YAAY,CAAC;AAClD,EAAC,KAAK,MAAc,WAAW;AAAA,IAC7B,GAAG;AAAA,IACH,SAAS,QAAQ,WAAW,SAAS;AAAA,IACrC,aAAa,QAAQ,eAAe,SAAS;AAAA,IAC7C,MAAM,QAAQ,QAAQ,SAAS;AAAA,IAC/B,eAAe;AAAA,EACjB;AAEA,SAAO;AACT;AAEA,IAAO,sBAAQ;AAMf,IAAI,OAAO,WAAW,eAAe,OAAO,SAAS;AACnD,SAAO,UAAU;AACjB,SAAO,QAAQ,UAAU;AAC3B;","names":[]}
1
+ {"version":3,"sources":["../src/expo-plugin.ts"],"sourcesContent":["/**\n * Expo config plugin.\n *\n * Apply via `app.json`:\n *\n * {\n * \"expo\": {\n * \"plugins\": [\n * [\"@allstak/react-native\", { \"release\": \"mobile@1.2.3\", \"environment\": \"production\" }]\n * ]\n * }\n * }\n *\n * The plugin runs at `expo prebuild` / EAS build time and:\n *\n * 1. Adds the AllStak iOS pod (declared in `react-native.config.js`).\n * Expo's autolinking already picks this up — we just record metadata\n * so `expo doctor` can verify the install is wired.\n * 2. Stamps the chosen `release` into `expo-constants` extras so the JS\n * layer can read it at runtime via `Constants.expoConfig.extra._allstak`\n * without the host app needing to plumb it through env vars.\n * 3. Records that `@allstak/react-native` was loaded as an Expo plugin\n * for diagnostics.\n *\n * Pure config mutation — no native code is patched here. The native iOS\n * + Android modules under ./native are linked by Expo's existing\n * autolinking flow (which honors the `react-native.config.js` manifest).\n */\n\nexport interface AllStakExpoOptions {\n /** Release identifier (e.g. `mobile@1.2.3`). Stamped into `Constants.expoConfig.extra._allstak.release`. */\n release?: string;\n /** Environment label — `production`, `staging`, etc. */\n environment?: string;\n /** Optional dist tag (e.g. `ios-hermes`). Auto-detected at runtime if unset. */\n dist?: string;\n}\n\ninterface ExpoConfig {\n name?: string;\n extra?: Record<string, unknown>;\n plugins?: any[];\n [key: string]: any;\n}\n\ninterface ExpoConfigContext {\n modResults?: any;\n modRawConfig?: ExpoConfig;\n [key: string]: any;\n}\n\n/**\n * The plugin function itself. Expo's plugin runner calls it as\n * `(config, options) => modifiedConfig`. We avoid importing\n * `@expo/config-plugins` so the package has zero hard dependencies on the\n * Expo toolchain — the type checking happens at usage site instead.\n */\nfunction withAllStak(config: ExpoConfig & ExpoConfigContext, options: AllStakExpoOptions = {}): ExpoConfig {\n const next: ExpoConfig = { ...config };\n next.extra = { ...(config.extra ?? {}) };\n\n // Embed runtime-readable metadata under a namespaced key so we never\n // collide with the host app's other extras.\n const existing = (next.extra as any)._allstak ?? {};\n (next.extra as any)._allstak = {\n ...existing,\n release: options.release ?? existing.release,\n environment: options.environment ?? existing.environment,\n dist: options.dist ?? existing.dist,\n pluginVersion: '0.3.3',\n };\n\n return next;\n}\n\nexport default withAllStak;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAyDA,SAAS,YAAY,QAAwC,UAA8B,CAAC,GAAe;AACzG,QAAM,OAAmB,EAAE,GAAG,OAAO;AACrC,OAAK,QAAQ,EAAE,GAAI,OAAO,SAAS,CAAC,EAAG;AAIvC,QAAM,WAAY,KAAK,MAAc,YAAY,CAAC;AAClD,EAAC,KAAK,MAAc,WAAW;AAAA,IAC7B,GAAG;AAAA,IACH,SAAS,QAAQ,WAAW,SAAS;AAAA,IACrC,aAAa,QAAQ,eAAe,SAAS;AAAA,IAC7C,MAAM,QAAQ,QAAQ,SAAS;AAAA,IAC/B,eAAe;AAAA,EACjB;AAEA,SAAO;AACT;AAEA,IAAO,sBAAQ;","names":[]}
@@ -10,15 +10,11 @@ function withAllStak(config, options = {}) {
10
10
  release: options.release ?? existing.release,
11
11
  environment: options.environment ?? existing.environment,
12
12
  dist: options.dist ?? existing.dist,
13
- pluginVersion: "0.3.1"
13
+ pluginVersion: "0.3.3"
14
14
  };
15
15
  return next;
16
16
  }
17
17
  var expo_plugin_default = withAllStak;
18
- if (typeof module !== "undefined" && module.exports) {
19
- module.exports = withAllStak;
20
- module.exports.default = withAllStak;
21
- }
22
18
  export {
23
19
  expo_plugin_default as default
24
20
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/expo-plugin.ts"],"sourcesContent":["/**\n * Expo config plugin.\n *\n * Apply via `app.json`:\n *\n * {\n * \"expo\": {\n * \"plugins\": [\n * [\"@allstak/react-native\", { \"release\": \"mobile@1.2.3\", \"environment\": \"production\" }]\n * ]\n * }\n * }\n *\n * The plugin runs at `expo prebuild` / EAS build time and:\n *\n * 1. Adds the AllStak iOS pod (declared in `react-native.config.js`).\n * Expo's autolinking already picks this up — we just record metadata\n * so `expo doctor` can verify the install is wired.\n * 2. Stamps the chosen `release` into `expo-constants` extras so the JS\n * layer can read it at runtime via `Constants.expoConfig.extra._allstak`\n * without the host app needing to plumb it through env vars.\n * 3. Records that `@allstak/react-native` was loaded as an Expo plugin\n * for diagnostics.\n *\n * Pure config mutation — no native code is patched here. The native iOS\n * + Android modules under ./native are linked by Expo's existing\n * autolinking flow (which honors the `react-native.config.js` manifest).\n */\n\nexport interface AllStakExpoOptions {\n /** Release identifier (e.g. `mobile@1.2.3`). Stamped into `Constants.expoConfig.extra._allstak.release`. */\n release?: string;\n /** Environment label — `production`, `staging`, etc. */\n environment?: string;\n /** Optional dist tag (e.g. `ios-hermes`). Auto-detected at runtime if unset. */\n dist?: string;\n}\n\ninterface ExpoConfig {\n name?: string;\n extra?: Record<string, unknown>;\n plugins?: any[];\n [key: string]: any;\n}\n\ninterface ExpoConfigContext {\n modResults?: any;\n modRawConfig?: ExpoConfig;\n [key: string]: any;\n}\n\n/**\n * The plugin function itself. Expo's plugin runner calls it as\n * `(config, options) => modifiedConfig`. We avoid importing\n * `@expo/config-plugins` so the package has zero hard dependencies on the\n * Expo toolchain — the type checking happens at usage site instead.\n */\nfunction withAllStak(config: ExpoConfig & ExpoConfigContext, options: AllStakExpoOptions = {}): ExpoConfig {\n const next: ExpoConfig = { ...config };\n next.extra = { ...(config.extra ?? {}) };\n\n // Embed runtime-readable metadata under a namespaced key so we never\n // collide with the host app's other extras.\n const existing = (next.extra as any)._allstak ?? {};\n (next.extra as any)._allstak = {\n ...existing,\n release: options.release ?? existing.release,\n environment: options.environment ?? existing.environment,\n dist: options.dist ?? existing.dist,\n pluginVersion: '0.3.1',\n };\n\n return next;\n}\n\nexport default withAllStak;\n\n// Expose as a CommonJS function so `app.plugin.js`'s `require('./dist/expo-plugin.js')`\n// returns the plugin directly. The `declare const module` keeps the TS\n// type-checker happy without dragging in @types/node.\ndeclare const module: { exports: any };\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = withAllStak;\n module.exports.default = withAllStak;\n}\n"],"mappings":";;;AAyDA,SAAS,YAAY,QAAwC,UAA8B,CAAC,GAAe;AACzG,QAAM,OAAmB,EAAE,GAAG,OAAO;AACrC,OAAK,QAAQ,EAAE,GAAI,OAAO,SAAS,CAAC,EAAG;AAIvC,QAAM,WAAY,KAAK,MAAc,YAAY,CAAC;AAClD,EAAC,KAAK,MAAc,WAAW;AAAA,IAC7B,GAAG;AAAA,IACH,SAAS,QAAQ,WAAW,SAAS;AAAA,IACrC,aAAa,QAAQ,eAAe,SAAS;AAAA,IAC7C,MAAM,QAAQ,QAAQ,SAAS;AAAA,IAC/B,eAAe;AAAA,EACjB;AAEA,SAAO;AACT;AAEA,IAAO,sBAAQ;AAMf,IAAI,OAAO,WAAW,eAAe,OAAO,SAAS;AACnD,SAAO,UAAU;AACjB,SAAO,QAAQ,UAAU;AAC3B;","names":[]}
1
+ {"version":3,"sources":["../src/expo-plugin.ts"],"sourcesContent":["/**\n * Expo config plugin.\n *\n * Apply via `app.json`:\n *\n * {\n * \"expo\": {\n * \"plugins\": [\n * [\"@allstak/react-native\", { \"release\": \"mobile@1.2.3\", \"environment\": \"production\" }]\n * ]\n * }\n * }\n *\n * The plugin runs at `expo prebuild` / EAS build time and:\n *\n * 1. Adds the AllStak iOS pod (declared in `react-native.config.js`).\n * Expo's autolinking already picks this up — we just record metadata\n * so `expo doctor` can verify the install is wired.\n * 2. Stamps the chosen `release` into `expo-constants` extras so the JS\n * layer can read it at runtime via `Constants.expoConfig.extra._allstak`\n * without the host app needing to plumb it through env vars.\n * 3. Records that `@allstak/react-native` was loaded as an Expo plugin\n * for diagnostics.\n *\n * Pure config mutation — no native code is patched here. The native iOS\n * + Android modules under ./native are linked by Expo's existing\n * autolinking flow (which honors the `react-native.config.js` manifest).\n */\n\nexport interface AllStakExpoOptions {\n /** Release identifier (e.g. `mobile@1.2.3`). Stamped into `Constants.expoConfig.extra._allstak.release`. */\n release?: string;\n /** Environment label — `production`, `staging`, etc. */\n environment?: string;\n /** Optional dist tag (e.g. `ios-hermes`). Auto-detected at runtime if unset. */\n dist?: string;\n}\n\ninterface ExpoConfig {\n name?: string;\n extra?: Record<string, unknown>;\n plugins?: any[];\n [key: string]: any;\n}\n\ninterface ExpoConfigContext {\n modResults?: any;\n modRawConfig?: ExpoConfig;\n [key: string]: any;\n}\n\n/**\n * The plugin function itself. Expo's plugin runner calls it as\n * `(config, options) => modifiedConfig`. We avoid importing\n * `@expo/config-plugins` so the package has zero hard dependencies on the\n * Expo toolchain — the type checking happens at usage site instead.\n */\nfunction withAllStak(config: ExpoConfig & ExpoConfigContext, options: AllStakExpoOptions = {}): ExpoConfig {\n const next: ExpoConfig = { ...config };\n next.extra = { ...(config.extra ?? {}) };\n\n // Embed runtime-readable metadata under a namespaced key so we never\n // collide with the host app's other extras.\n const existing = (next.extra as any)._allstak ?? {};\n (next.extra as any)._allstak = {\n ...existing,\n release: options.release ?? existing.release,\n environment: options.environment ?? existing.environment,\n dist: options.dist ?? existing.dist,\n pluginVersion: '0.3.3',\n };\n\n return next;\n}\n\nexport default withAllStak;\n"],"mappings":";;;AAyDA,SAAS,YAAY,QAAwC,UAA8B,CAAC,GAAe;AACzG,QAAM,OAAmB,EAAE,GAAG,OAAO;AACrC,OAAK,QAAQ,EAAE,GAAI,OAAO,SAAS,CAAC,EAAG;AAIvC,QAAM,WAAY,KAAK,MAAc,YAAY,CAAC;AAClD,EAAC,KAAK,MAAc,WAAW;AAAA,IAC7B,GAAG;AAAA,IACH,SAAS,QAAQ,WAAW,SAAS;AAAA,IACrC,aAAa,QAAQ,eAAe,SAAS;AAAA,IAC7C,MAAM,QAAQ,QAAQ,SAAS;AAAA,IAC/B,eAAe;AAAA,EACjB;AAEA,SAAO;AACT;AAEA,IAAO,sBAAQ;","names":[]}
package/dist/index.d.mts CHANGED
@@ -440,7 +440,7 @@ declare function __resetConsoleInstrumentationFlagForTest(): void;
440
440
 
441
441
  declare const INGEST_HOST = "https://api.allstak.sa";
442
442
  declare const SDK_NAME = "allstak-react-native";
443
- declare const SDK_VERSION = "0.3.1";
443
+ declare const SDK_VERSION = "0.3.3";
444
444
 
445
445
  interface AllStakConfig {
446
446
  /** Project API key (`ask_live_…`). Required. */