@allstak/react 0.2.1 → 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
@@ -114,6 +114,49 @@ import { AllStak } from '@allstak/react';
114
114
  AllStak.setUser({ id: user.id, email: user.email });
115
115
  ```
116
116
 
117
+ ## HTTP tracking
118
+
119
+ Setting `enableHttpTracking: true` (off by default) auto-wraps `fetch`,
120
+ `XMLHttpRequest`, and `axios` (when installed) so every outbound HTTP
121
+ call is recorded as an `http_request` event.
122
+
123
+ **Privacy defaults are aggressive:**
124
+
125
+ - request/response bodies are **not** captured
126
+ - headers are **not** captured
127
+ - `Authorization`, `Cookie`, `Set-Cookie`, `X-API-Key`, `X-Auth-Token`,
128
+ `Proxy-Authorization` are **always** redacted
129
+ - query params named `token`, `password`, `api_key`, `apikey`,
130
+ `authorization`, `auth`, `secret`, `access_token`, `refresh_token`,
131
+ `session`, `sessionid`, `jwt` are **always** redacted in the URL
132
+
133
+ To enable richer capture (only on routes you control):
134
+
135
+ ```ts
136
+ AllStak.init({
137
+ apiKey: '...',
138
+ enableHttpTracking: true,
139
+ httpTracking: {
140
+ captureRequestBody: true,
141
+ captureResponseBody: true,
142
+ captureHeaders: true, // auth headers still hard-redacted
143
+ redactHeaders: ['x-tenant'],
144
+ redactQueryParams: ['custom_id'],
145
+ ignoredUrls: [/health/i, '/metrics'],
146
+ allowedUrls: [],
147
+ maxBodyBytes: 4096,
148
+ },
149
+ });
150
+
151
+ // axios with custom adapter (rare):
152
+ import axios from 'axios';
153
+ const api = AllStak.instrumentAxios(axios.create({ baseURL: 'https://api.example.com' }));
154
+ ```
155
+
156
+ When an exception fires after a failed request, the most recent failed
157
+ HTTP requests (last 10) are automatically attached to the error
158
+ metadata under `http.recentFailed` for easy triage.
159
+
117
160
  ## Production Endpoint
118
161
 
119
162
  Production endpoint: `https://api.allstak.sa`. Override via `host` for self-hosted installs:
package/dist/index.d.mts CHANGED
@@ -1,5 +1,113 @@
1
1
  import * as React from 'react';
2
2
 
3
+ /**
4
+ * Minimal HTTP transport for React Native. Uses the global `fetch` (always
5
+ * present in RN >= 0.60) with a 3s timeout. Failed sends fall into a small
6
+ * in-memory ring buffer that we retry on the next successful flush.
7
+ *
8
+ * No window, no AbortController fallback shims — RN exposes both natively.
9
+ */
10
+ declare class HttpTransport {
11
+ private baseUrl;
12
+ private apiKey;
13
+ private buffer;
14
+ private flushing;
15
+ constructor(baseUrl: string, apiKey: string);
16
+ send(path: string, payload: unknown): Promise<void>;
17
+ private doFetch;
18
+ private flushBuffer;
19
+ getBufferSize(): number;
20
+ /**
21
+ * Wait for the in-flight retry-buffer to drain. Resolves `true` if the
22
+ * buffer empties within `timeoutMs` (default 2000ms), `false` otherwise.
23
+ * Useful at process exit / before navigation away.
24
+ */
25
+ flush(timeoutMs?: number): Promise<boolean>;
26
+ }
27
+
28
+ /**
29
+ * HTTP request batching + transport for the React Native SDK.
30
+ *
31
+ * Mirrors the wire shape used by `@allstak/js`'s HttpRequestModule so the
32
+ * existing backend ingest endpoint (`/ingest/v1/http-requests`) accepts
33
+ * events from this SDK without a schema change. Adds optional rich fields
34
+ * (headers, bodies, error string) — these ride alongside the existing
35
+ * required fields and are tolerated as additive metadata server-side.
36
+ *
37
+ * Batching:
38
+ * - flushes on a 5s timer OR when 20 events queue up
39
+ * - flushes immediately on `destroy()`
40
+ * - `getRecentFailed(n)` returns the last n failed requests (statusCode
41
+ * >= 400 OR error set), used by error-linking on the next captureException
42
+ */
43
+
44
+ /** What instrumentation hands to the module per request. */
45
+ interface HttpRequestEvent {
46
+ type: 'http_request';
47
+ method: string;
48
+ url: string;
49
+ statusCode?: number;
50
+ durationMs: number;
51
+ requestSize?: number;
52
+ responseSize?: number;
53
+ requestBody?: string;
54
+ responseBody?: string;
55
+ requestHeaders?: Record<string, string>;
56
+ responseHeaders?: Record<string, string>;
57
+ error?: string;
58
+ traceId?: string;
59
+ }
60
+ interface HttpRequestIngestItem {
61
+ type: 'http_request';
62
+ traceId: string;
63
+ direction: 'outbound';
64
+ method: string;
65
+ host: string;
66
+ path: string;
67
+ url: string;
68
+ statusCode: number;
69
+ durationMs: number;
70
+ requestSize?: number;
71
+ responseSize?: number;
72
+ requestBody?: string;
73
+ responseBody?: string;
74
+ requestHeaders?: Record<string, string>;
75
+ responseHeaders?: Record<string, string>;
76
+ error?: string;
77
+ environment?: string;
78
+ release?: string;
79
+ dist?: string;
80
+ platform?: string;
81
+ 'sdk.name'?: string;
82
+ 'sdk.version'?: string;
83
+ timestamp: string;
84
+ }
85
+ interface ModuleDefaults {
86
+ environment?: string;
87
+ release?: string;
88
+ dist?: string;
89
+ platform?: string;
90
+ sdkName?: string;
91
+ sdkVersion?: string;
92
+ }
93
+ declare class HttpRequestModule {
94
+ private transport;
95
+ private queue;
96
+ private recentFailed;
97
+ private flushTimer;
98
+ private destroyed;
99
+ private defaults;
100
+ constructor(transport: HttpTransport);
101
+ setDefaults(defaults: ModuleDefaults): void;
102
+ capture(ev: HttpRequestEvent): void;
103
+ /** Snapshot of the last failed requests for error-linking. Newest last. */
104
+ getRecentFailed(): ReadonlyArray<HttpRequestIngestItem>;
105
+ flush(): void;
106
+ destroy(): void;
107
+ /** @internal — for tests. */
108
+ getQueueSize(): number;
109
+ }
110
+
3
111
  /**
4
112
  * Per-call scoped context isolation.
5
113
  *
@@ -41,31 +149,6 @@ declare class Scope {
41
149
  clear(): this;
42
150
  }
43
151
 
44
- /**
45
- * Minimal HTTP transport for React Native. Uses the global `fetch` (always
46
- * present in RN >= 0.60) with a 3s timeout. Failed sends fall into a small
47
- * in-memory ring buffer that we retry on the next successful flush.
48
- *
49
- * No window, no AbortController fallback shims — RN exposes both natively.
50
- */
51
- declare class HttpTransport {
52
- private baseUrl;
53
- private apiKey;
54
- private buffer;
55
- private flushing;
56
- constructor(baseUrl: string, apiKey: string);
57
- send(path: string, payload: unknown): Promise<void>;
58
- private doFetch;
59
- private flushBuffer;
60
- getBufferSize(): number;
61
- /**
62
- * Wait for the in-flight retry-buffer to drain. Resolves `true` if the
63
- * buffer empties within `timeoutMs` (default 2000ms), `false` otherwise.
64
- * Useful at process exit / before navigation away.
65
- */
66
- flush(timeoutMs?: number): Promise<boolean>;
67
- }
68
-
69
152
  /**
70
153
  * Lightweight distributed tracing primitives.
71
154
  *
@@ -192,19 +275,37 @@ declare class ReplayRecorder {
192
275
  private safeAttribute;
193
276
  }
194
277
 
195
- /**
196
- * Standalone AllStak client for the browser/React environment. No external
197
- * AllStak SDK dependencies — only the browser's native `fetch`, AbortController,
198
- * Date, JSON, and (optionally) `window` for unhandled error auto-capture.
199
- *
200
- * Surface mirrors the public AllStak API used by web apps:
201
- * init / captureException / captureMessage / addBreadcrumb / clearBreadcrumbs
202
- * setUser / setTag / setIdentity / getSessionId
203
- */
278
+ interface HttpTrackingOptions {
279
+ /** Capture request body. Default false. Truncated to maxBodyBytes. */
280
+ captureRequestBody?: boolean;
281
+ /** Capture response body. Default false. Truncated to maxBodyBytes. */
282
+ captureResponseBody?: boolean;
283
+ /**
284
+ * Capture request + response headers. Default false. Hard-redacted
285
+ * names are always stripped regardless of this flag.
286
+ */
287
+ captureHeaders?: boolean;
288
+ /** Additional header names to redact (case-insensitive). */
289
+ redactHeaders?: string[];
290
+ /** Additional query-param names to redact (case-insensitive). */
291
+ redactQueryParams?: string[];
292
+ /**
293
+ * Skip URLs matching any of these patterns. String = case-insensitive
294
+ * substring match; RegExp = `.test()` against the full URL.
295
+ */
296
+ ignoredUrls?: (string | RegExp)[];
297
+ /**
298
+ * If non-empty, only capture URLs matching at least one of these
299
+ * patterns. Takes precedence over ignoredUrls.
300
+ */
301
+ allowedUrls?: (string | RegExp)[];
302
+ /** Max bytes per captured body. Default 4096. */
303
+ maxBodyBytes?: number;
304
+ }
204
305
 
205
306
  declare const INGEST_HOST = "https://api.allstak.sa";
206
307
  declare const SDK_NAME = "allstak-react";
207
- declare const SDK_VERSION = "0.2.1";
308
+ declare const SDK_VERSION = "0.3.0";
208
309
 
209
310
  interface AllStakConfig {
210
311
  /** Project API key (`ask_live_…`). Required. */
@@ -252,6 +353,17 @@ interface AllStakConfig {
252
353
  * the full privacy contract.
253
354
  */
254
355
  replay?: ReplayOptions;
356
+ /**
357
+ * Auto-instrument outbound HTTP — wraps `fetch`, `XMLHttpRequest`, and
358
+ * (when present) `axios`. Default: false.
359
+ */
360
+ enableHttpTracking?: boolean;
361
+ /**
362
+ * Privacy + capture controls for HTTP instrumentation. Bodies and
363
+ * headers are OFF by default; auth headers and sensitive query params
364
+ * are ALWAYS redacted.
365
+ */
366
+ httpTracking?: HttpTrackingOptions;
255
367
  /**
256
368
  * Probability in [0, 1] that any given error is sent. Default: 1 (no sampling).
257
369
  * Applied per event before {@link beforeSend}.
@@ -324,9 +436,15 @@ declare class AllStakClient {
324
436
  private scopeStack;
325
437
  private tracing;
326
438
  private replay;
439
+ private httpRequests;
440
+ private _instrumentAxios;
327
441
  private onErrorHandler;
328
442
  private onRejectionHandler;
329
443
  constructor(config: AllStakConfig);
444
+ /** Manually instrument an axios instance. No-op when HTTP tracking is off. */
445
+ instrumentAxios<T = any>(axios: T): T;
446
+ /** Snapshot of recent failed HTTP requests for error-linking. */
447
+ getRecentFailedHttp(): readonly HttpRequestIngestItem[];
330
448
  captureException(error: Error, context?: Record<string, unknown>): void;
331
449
  /** Start a new span — auto-parented to any currently-active span. */
332
450
  startSpan(operation: string, options?: {
@@ -450,6 +568,8 @@ declare const AllStak: {
450
568
  setTraceId(traceId: string): void;
451
569
  getCurrentSpanId(): string | null;
452
570
  resetTrace(): void;
571
+ /** Manually instrument an axios instance. No-op when HTTP tracking is off. */
572
+ instrumentAxios<T = any>(axios: T): T;
453
573
  getSessionId(): string;
454
574
  getConfig(): AllStakConfig | null;
455
575
  destroy(): void;
@@ -549,4 +669,4 @@ declare function useAllStak(): {
549
669
  */
550
670
  declare function withAllStakProfiler<P extends object>(Component: React.ComponentType<P>, name?: string): React.FC<P>;
551
671
 
552
- export { AllStak, AllStakClient, type AllStakConfig, AllStakErrorBoundary, type AllStakErrorBoundaryProps, type Breadcrumb, INGEST_HOST, type ReplayOptions, ReplayRecorder, SDK_NAME, SDK_VERSION, Scope, instrumentBrowserNavigation, instrumentConsole, instrumentFetch, instrumentNextRouter, instrumentReactRouter, useAllStak, withAllStakProfiler };
672
+ export { AllStak, AllStakClient, type AllStakConfig, AllStakErrorBoundary, type AllStakErrorBoundaryProps, type Breadcrumb, type HttpRequestEvent, HttpRequestModule, type HttpTrackingOptions, INGEST_HOST, type ReplayOptions, ReplayRecorder, SDK_NAME, SDK_VERSION, Scope, instrumentBrowserNavigation, instrumentConsole, instrumentFetch, instrumentNextRouter, instrumentReactRouter, useAllStak, withAllStakProfiler };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,113 @@
1
1
  import * as React from 'react';
2
2
 
3
+ /**
4
+ * Minimal HTTP transport for React Native. Uses the global `fetch` (always
5
+ * present in RN >= 0.60) with a 3s timeout. Failed sends fall into a small
6
+ * in-memory ring buffer that we retry on the next successful flush.
7
+ *
8
+ * No window, no AbortController fallback shims — RN exposes both natively.
9
+ */
10
+ declare class HttpTransport {
11
+ private baseUrl;
12
+ private apiKey;
13
+ private buffer;
14
+ private flushing;
15
+ constructor(baseUrl: string, apiKey: string);
16
+ send(path: string, payload: unknown): Promise<void>;
17
+ private doFetch;
18
+ private flushBuffer;
19
+ getBufferSize(): number;
20
+ /**
21
+ * Wait for the in-flight retry-buffer to drain. Resolves `true` if the
22
+ * buffer empties within `timeoutMs` (default 2000ms), `false` otherwise.
23
+ * Useful at process exit / before navigation away.
24
+ */
25
+ flush(timeoutMs?: number): Promise<boolean>;
26
+ }
27
+
28
+ /**
29
+ * HTTP request batching + transport for the React Native SDK.
30
+ *
31
+ * Mirrors the wire shape used by `@allstak/js`'s HttpRequestModule so the
32
+ * existing backend ingest endpoint (`/ingest/v1/http-requests`) accepts
33
+ * events from this SDK without a schema change. Adds optional rich fields
34
+ * (headers, bodies, error string) — these ride alongside the existing
35
+ * required fields and are tolerated as additive metadata server-side.
36
+ *
37
+ * Batching:
38
+ * - flushes on a 5s timer OR when 20 events queue up
39
+ * - flushes immediately on `destroy()`
40
+ * - `getRecentFailed(n)` returns the last n failed requests (statusCode
41
+ * >= 400 OR error set), used by error-linking on the next captureException
42
+ */
43
+
44
+ /** What instrumentation hands to the module per request. */
45
+ interface HttpRequestEvent {
46
+ type: 'http_request';
47
+ method: string;
48
+ url: string;
49
+ statusCode?: number;
50
+ durationMs: number;
51
+ requestSize?: number;
52
+ responseSize?: number;
53
+ requestBody?: string;
54
+ responseBody?: string;
55
+ requestHeaders?: Record<string, string>;
56
+ responseHeaders?: Record<string, string>;
57
+ error?: string;
58
+ traceId?: string;
59
+ }
60
+ interface HttpRequestIngestItem {
61
+ type: 'http_request';
62
+ traceId: string;
63
+ direction: 'outbound';
64
+ method: string;
65
+ host: string;
66
+ path: string;
67
+ url: string;
68
+ statusCode: number;
69
+ durationMs: number;
70
+ requestSize?: number;
71
+ responseSize?: number;
72
+ requestBody?: string;
73
+ responseBody?: string;
74
+ requestHeaders?: Record<string, string>;
75
+ responseHeaders?: Record<string, string>;
76
+ error?: string;
77
+ environment?: string;
78
+ release?: string;
79
+ dist?: string;
80
+ platform?: string;
81
+ 'sdk.name'?: string;
82
+ 'sdk.version'?: string;
83
+ timestamp: string;
84
+ }
85
+ interface ModuleDefaults {
86
+ environment?: string;
87
+ release?: string;
88
+ dist?: string;
89
+ platform?: string;
90
+ sdkName?: string;
91
+ sdkVersion?: string;
92
+ }
93
+ declare class HttpRequestModule {
94
+ private transport;
95
+ private queue;
96
+ private recentFailed;
97
+ private flushTimer;
98
+ private destroyed;
99
+ private defaults;
100
+ constructor(transport: HttpTransport);
101
+ setDefaults(defaults: ModuleDefaults): void;
102
+ capture(ev: HttpRequestEvent): void;
103
+ /** Snapshot of the last failed requests for error-linking. Newest last. */
104
+ getRecentFailed(): ReadonlyArray<HttpRequestIngestItem>;
105
+ flush(): void;
106
+ destroy(): void;
107
+ /** @internal — for tests. */
108
+ getQueueSize(): number;
109
+ }
110
+
3
111
  /**
4
112
  * Per-call scoped context isolation.
5
113
  *
@@ -41,31 +149,6 @@ declare class Scope {
41
149
  clear(): this;
42
150
  }
43
151
 
44
- /**
45
- * Minimal HTTP transport for React Native. Uses the global `fetch` (always
46
- * present in RN >= 0.60) with a 3s timeout. Failed sends fall into a small
47
- * in-memory ring buffer that we retry on the next successful flush.
48
- *
49
- * No window, no AbortController fallback shims — RN exposes both natively.
50
- */
51
- declare class HttpTransport {
52
- private baseUrl;
53
- private apiKey;
54
- private buffer;
55
- private flushing;
56
- constructor(baseUrl: string, apiKey: string);
57
- send(path: string, payload: unknown): Promise<void>;
58
- private doFetch;
59
- private flushBuffer;
60
- getBufferSize(): number;
61
- /**
62
- * Wait for the in-flight retry-buffer to drain. Resolves `true` if the
63
- * buffer empties within `timeoutMs` (default 2000ms), `false` otherwise.
64
- * Useful at process exit / before navigation away.
65
- */
66
- flush(timeoutMs?: number): Promise<boolean>;
67
- }
68
-
69
152
  /**
70
153
  * Lightweight distributed tracing primitives.
71
154
  *
@@ -192,19 +275,37 @@ declare class ReplayRecorder {
192
275
  private safeAttribute;
193
276
  }
194
277
 
195
- /**
196
- * Standalone AllStak client for the browser/React environment. No external
197
- * AllStak SDK dependencies — only the browser's native `fetch`, AbortController,
198
- * Date, JSON, and (optionally) `window` for unhandled error auto-capture.
199
- *
200
- * Surface mirrors the public AllStak API used by web apps:
201
- * init / captureException / captureMessage / addBreadcrumb / clearBreadcrumbs
202
- * setUser / setTag / setIdentity / getSessionId
203
- */
278
+ interface HttpTrackingOptions {
279
+ /** Capture request body. Default false. Truncated to maxBodyBytes. */
280
+ captureRequestBody?: boolean;
281
+ /** Capture response body. Default false. Truncated to maxBodyBytes. */
282
+ captureResponseBody?: boolean;
283
+ /**
284
+ * Capture request + response headers. Default false. Hard-redacted
285
+ * names are always stripped regardless of this flag.
286
+ */
287
+ captureHeaders?: boolean;
288
+ /** Additional header names to redact (case-insensitive). */
289
+ redactHeaders?: string[];
290
+ /** Additional query-param names to redact (case-insensitive). */
291
+ redactQueryParams?: string[];
292
+ /**
293
+ * Skip URLs matching any of these patterns. String = case-insensitive
294
+ * substring match; RegExp = `.test()` against the full URL.
295
+ */
296
+ ignoredUrls?: (string | RegExp)[];
297
+ /**
298
+ * If non-empty, only capture URLs matching at least one of these
299
+ * patterns. Takes precedence over ignoredUrls.
300
+ */
301
+ allowedUrls?: (string | RegExp)[];
302
+ /** Max bytes per captured body. Default 4096. */
303
+ maxBodyBytes?: number;
304
+ }
204
305
 
205
306
  declare const INGEST_HOST = "https://api.allstak.sa";
206
307
  declare const SDK_NAME = "allstak-react";
207
- declare const SDK_VERSION = "0.2.1";
308
+ declare const SDK_VERSION = "0.3.0";
208
309
 
209
310
  interface AllStakConfig {
210
311
  /** Project API key (`ask_live_…`). Required. */
@@ -252,6 +353,17 @@ interface AllStakConfig {
252
353
  * the full privacy contract.
253
354
  */
254
355
  replay?: ReplayOptions;
356
+ /**
357
+ * Auto-instrument outbound HTTP — wraps `fetch`, `XMLHttpRequest`, and
358
+ * (when present) `axios`. Default: false.
359
+ */
360
+ enableHttpTracking?: boolean;
361
+ /**
362
+ * Privacy + capture controls for HTTP instrumentation. Bodies and
363
+ * headers are OFF by default; auth headers and sensitive query params
364
+ * are ALWAYS redacted.
365
+ */
366
+ httpTracking?: HttpTrackingOptions;
255
367
  /**
256
368
  * Probability in [0, 1] that any given error is sent. Default: 1 (no sampling).
257
369
  * Applied per event before {@link beforeSend}.
@@ -324,9 +436,15 @@ declare class AllStakClient {
324
436
  private scopeStack;
325
437
  private tracing;
326
438
  private replay;
439
+ private httpRequests;
440
+ private _instrumentAxios;
327
441
  private onErrorHandler;
328
442
  private onRejectionHandler;
329
443
  constructor(config: AllStakConfig);
444
+ /** Manually instrument an axios instance. No-op when HTTP tracking is off. */
445
+ instrumentAxios<T = any>(axios: T): T;
446
+ /** Snapshot of recent failed HTTP requests for error-linking. */
447
+ getRecentFailedHttp(): readonly HttpRequestIngestItem[];
330
448
  captureException(error: Error, context?: Record<string, unknown>): void;
331
449
  /** Start a new span — auto-parented to any currently-active span. */
332
450
  startSpan(operation: string, options?: {
@@ -450,6 +568,8 @@ declare const AllStak: {
450
568
  setTraceId(traceId: string): void;
451
569
  getCurrentSpanId(): string | null;
452
570
  resetTrace(): void;
571
+ /** Manually instrument an axios instance. No-op when HTTP tracking is off. */
572
+ instrumentAxios<T = any>(axios: T): T;
453
573
  getSessionId(): string;
454
574
  getConfig(): AllStakConfig | null;
455
575
  destroy(): void;
@@ -549,4 +669,4 @@ declare function useAllStak(): {
549
669
  */
550
670
  declare function withAllStakProfiler<P extends object>(Component: React.ComponentType<P>, name?: string): React.FC<P>;
551
671
 
552
- export { AllStak, AllStakClient, type AllStakConfig, AllStakErrorBoundary, type AllStakErrorBoundaryProps, type Breadcrumb, INGEST_HOST, type ReplayOptions, ReplayRecorder, SDK_NAME, SDK_VERSION, Scope, instrumentBrowserNavigation, instrumentConsole, instrumentFetch, instrumentNextRouter, instrumentReactRouter, useAllStak, withAllStakProfiler };
672
+ export { AllStak, AllStakClient, type AllStakConfig, AllStakErrorBoundary, type AllStakErrorBoundaryProps, type Breadcrumb, type HttpRequestEvent, HttpRequestModule, type HttpTrackingOptions, INGEST_HOST, type ReplayOptions, ReplayRecorder, SDK_NAME, SDK_VERSION, Scope, instrumentBrowserNavigation, instrumentConsole, instrumentFetch, instrumentNextRouter, instrumentReactRouter, useAllStak, withAllStakProfiler };