@allstak/react-native 0.1.3 → 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 CHANGED
@@ -45,19 +45,86 @@ npm install @allstak/react-native
45
45
  > Create a project at [app.allstak.sa](https://app.allstak.sa) to get your API key.
46
46
 
47
47
  ```ts
48
- import { installReactNative, AllStak } from '@allstak/react-native';
48
+ import { Platform } from 'react-native';
49
+ import { AllStak, installReactNative } from '@allstak/react-native';
49
50
 
50
- installReactNative({
51
+ AllStak.init({
51
52
  apiKey: process.env.ALLSTAK_API_KEY!,
52
53
  environment: 'production',
53
- release: 'mobile@1.0.0',
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
54
57
  });
58
+ installReactNative(); // ErrorUtils + Hermes promise rejections + Platform tags
55
59
 
56
60
  AllStak.captureException(new Error('test: hello from allstak-react-native'));
57
61
  ```
58
62
 
59
63
  Run the app — the test error appears in your dashboard within seconds.
60
64
 
65
+ ## HTTP tracking
66
+
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.
70
+
71
+ **Privacy defaults are aggressive — `enableHttpTracking: true` is safe
72
+ to ship to production:**
73
+
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
83
+
84
+ Enable richer capture only on routes you control:
85
+
86
+ ```ts
87
+ 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
+ },
100
+ });
101
+ ```
102
+
103
+ ### axios
104
+
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:
107
+
108
+ ```ts
109
+ import axios from 'axios';
110
+ const api = AllStak.instrumentAxios(axios.create({ baseURL: 'https://api.example.com' }));
111
+ ```
112
+
113
+ ### Errors auto-link to recent failed requests
114
+
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`.
121
+
122
+ ### `dist`
123
+
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.
127
+
61
128
  ## Get Your API Key
62
129
 
63
130
  1. Sign up at [app.allstak.sa](https://app.allstak.sa)
@@ -109,6 +176,44 @@ Production endpoint: `https://api.allstak.sa`. Override via `host` for self-host
109
176
  installReactNative({ apiKey: '...', host: 'https://allstak.mycorp.com' });
110
177
  ```
111
178
 
179
+ ## Source maps (Hermes / Metro)
180
+
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):
185
+
186
+ ```bash
187
+ npm install -D @allstak/js
188
+ ```
189
+
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
196
+
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
200
+
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 }))"
205
+ ```
206
+
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.
210
+
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).
216
+
112
217
  ## Links
113
218
 
114
219
  - Documentation: https://docs.allstak.sa
package/app.plugin.js ADDED
@@ -0,0 +1,6 @@
1
+ // Expo entrypoint — re-exports the actual plugin from `dist/expo-plugin.js`
2
+ // so consumers can write `"plugins": ["@allstak/react-native"]` in their
3
+ // app.json and Expo's `withPlugins` resolver finds it without an extra
4
+ // import path. Expo looks for `app.plugin.js` at the package root by
5
+ // convention.
6
+ module.exports = require('./dist/expo-plugin.js');
@@ -0,0 +1,11 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ export {
9
+ __require
10
+ };
11
+ //# sourceMappingURL=chunk-BJTO5JO5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Expo config plugin.
3
+ *
4
+ * Apply via `app.json`:
5
+ *
6
+ * {
7
+ * "expo": {
8
+ * "plugins": [
9
+ * ["@allstak/react-native", { "release": "mobile@1.2.3", "environment": "production" }]
10
+ * ]
11
+ * }
12
+ * }
13
+ *
14
+ * The plugin runs at `expo prebuild` / EAS build time and:
15
+ *
16
+ * 1. Adds the AllStak iOS pod (declared in `react-native.config.js`).
17
+ * Expo's autolinking already picks this up — we just record metadata
18
+ * so `expo doctor` can verify the install is wired.
19
+ * 2. Stamps the chosen `release` into `expo-constants` extras so the JS
20
+ * layer can read it at runtime via `Constants.expoConfig.extra._allstak`
21
+ * without the host app needing to plumb it through env vars.
22
+ * 3. Records that `@allstak/react-native` was loaded as an Expo plugin
23
+ * for diagnostics.
24
+ *
25
+ * Pure config mutation — no native code is patched here. The native iOS
26
+ * + Android modules under ./native are linked by Expo's existing
27
+ * autolinking flow (which honors the `react-native.config.js` manifest).
28
+ */
29
+ interface AllStakExpoOptions {
30
+ /** Release identifier (e.g. `mobile@1.2.3`). Stamped into `Constants.expoConfig.extra._allstak.release`. */
31
+ release?: string;
32
+ /** Environment label — `production`, `staging`, etc. */
33
+ environment?: string;
34
+ /** Optional dist tag (e.g. `ios-hermes`). Auto-detected at runtime if unset. */
35
+ dist?: string;
36
+ }
37
+ interface ExpoConfig {
38
+ name?: string;
39
+ extra?: Record<string, unknown>;
40
+ plugins?: any[];
41
+ [key: string]: any;
42
+ }
43
+ interface ExpoConfigContext {
44
+ modResults?: any;
45
+ modRawConfig?: ExpoConfig;
46
+ [key: string]: any;
47
+ }
48
+ /**
49
+ * The plugin function itself. Expo's plugin runner calls it as
50
+ * `(config, options) => modifiedConfig`. We avoid importing
51
+ * `@expo/config-plugins` so the package has zero hard dependencies on the
52
+ * Expo toolchain — the type checking happens at usage site instead.
53
+ */
54
+ declare function withAllStak(config: ExpoConfig & ExpoConfigContext, options?: AllStakExpoOptions): ExpoConfig;
55
+
56
+ export { type AllStakExpoOptions, withAllStak as default };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Expo config plugin.
3
+ *
4
+ * Apply via `app.json`:
5
+ *
6
+ * {
7
+ * "expo": {
8
+ * "plugins": [
9
+ * ["@allstak/react-native", { "release": "mobile@1.2.3", "environment": "production" }]
10
+ * ]
11
+ * }
12
+ * }
13
+ *
14
+ * The plugin runs at `expo prebuild` / EAS build time and:
15
+ *
16
+ * 1. Adds the AllStak iOS pod (declared in `react-native.config.js`).
17
+ * Expo's autolinking already picks this up — we just record metadata
18
+ * so `expo doctor` can verify the install is wired.
19
+ * 2. Stamps the chosen `release` into `expo-constants` extras so the JS
20
+ * layer can read it at runtime via `Constants.expoConfig.extra._allstak`
21
+ * without the host app needing to plumb it through env vars.
22
+ * 3. Records that `@allstak/react-native` was loaded as an Expo plugin
23
+ * for diagnostics.
24
+ *
25
+ * Pure config mutation — no native code is patched here. The native iOS
26
+ * + Android modules under ./native are linked by Expo's existing
27
+ * autolinking flow (which honors the `react-native.config.js` manifest).
28
+ */
29
+ interface AllStakExpoOptions {
30
+ /** Release identifier (e.g. `mobile@1.2.3`). Stamped into `Constants.expoConfig.extra._allstak.release`. */
31
+ release?: string;
32
+ /** Environment label — `production`, `staging`, etc. */
33
+ environment?: string;
34
+ /** Optional dist tag (e.g. `ios-hermes`). Auto-detected at runtime if unset. */
35
+ dist?: string;
36
+ }
37
+ interface ExpoConfig {
38
+ name?: string;
39
+ extra?: Record<string, unknown>;
40
+ plugins?: any[];
41
+ [key: string]: any;
42
+ }
43
+ interface ExpoConfigContext {
44
+ modResults?: any;
45
+ modRawConfig?: ExpoConfig;
46
+ [key: string]: any;
47
+ }
48
+ /**
49
+ * The plugin function itself. Expo's plugin runner calls it as
50
+ * `(config, options) => modifiedConfig`. We avoid importing
51
+ * `@expo/config-plugins` so the package has zero hard dependencies on the
52
+ * Expo toolchain — the type checking happens at usage site instead.
53
+ */
54
+ declare function withAllStak(config: ExpoConfig & ExpoConfigContext, options?: AllStakExpoOptions): ExpoConfig;
55
+
56
+ export { type AllStakExpoOptions, withAllStak as default };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/expo-plugin.ts
21
+ var expo_plugin_exports = {};
22
+ __export(expo_plugin_exports, {
23
+ default: () => expo_plugin_default
24
+ });
25
+ module.exports = __toCommonJS(expo_plugin_exports);
26
+ function withAllStak(config, options = {}) {
27
+ const next = { ...config };
28
+ next.extra = { ...config.extra ?? {} };
29
+ const existing = next.extra._allstak ?? {};
30
+ next.extra._allstak = {
31
+ ...existing,
32
+ release: options.release ?? existing.release,
33
+ environment: options.environment ?? existing.environment,
34
+ dist: options.dist ?? existing.dist,
35
+ pluginVersion: "0.3.0"
36
+ };
37
+ return next;
38
+ }
39
+ var expo_plugin_default = withAllStak;
40
+ if (typeof module !== "undefined" && module.exports) {
41
+ module.exports = withAllStak;
42
+ module.exports.default = withAllStak;
43
+ }
44
+ //# sourceMappingURL=expo-plugin.js.map
@@ -0,0 +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.0',\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":[]}
@@ -0,0 +1,25 @@
1
+ import "./chunk-BJTO5JO5.mjs";
2
+
3
+ // src/expo-plugin.ts
4
+ function withAllStak(config, options = {}) {
5
+ const next = { ...config };
6
+ next.extra = { ...config.extra ?? {} };
7
+ const existing = next.extra._allstak ?? {};
8
+ next.extra._allstak = {
9
+ ...existing,
10
+ release: options.release ?? existing.release,
11
+ environment: options.environment ?? existing.environment,
12
+ dist: options.dist ?? existing.dist,
13
+ pluginVersion: "0.3.0"
14
+ };
15
+ return next;
16
+ }
17
+ var expo_plugin_default = withAllStak;
18
+ if (typeof module !== "undefined" && module.exports) {
19
+ module.exports = withAllStak;
20
+ module.exports.default = withAllStak;
21
+ }
22
+ export {
23
+ expo_plugin_default as default
24
+ };
25
+ //# sourceMappingURL=expo-plugin.mjs.map
@@ -0,0 +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.0',\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":[]}