@flashlog/tracker-sdk 0.1.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 ADDED
@@ -0,0 +1,177 @@
1
+ # @flashlog/tracker-sdk
2
+
3
+ The official browser-side SDK for **Flashlog** — a powerful bug tracking, real-time error reporting, and session replay platform.
4
+
5
+ Easily capture JavaScript exceptions, network failures, and WebSocket errors, while correlating frontend errors to backend logs with trace propagation. Include session replay recordings for debugging user journeys with high-fidelity visual playback.
6
+
7
+ ---
8
+
9
+ ## Key Features
10
+
11
+ * **Error Monitoring**: Automatically captures unhandled runtime errors, promise rejections, and console errors.
12
+ * **Network & Socket Tracking**: Records HTTP (fetch/XHR) failures, response statuses, and WebSocket connection errors/abnormal closures.
13
+ * **Session Replay (rrweb)**: Records high-fidelity user sessions with custom flushing, masking, and chunking options.
14
+ * **Trace Propagation**: Correlates frontend network requests with backend logs by injecting trace headers (e.g., `traceparent`, `x-flashlog-trace-id`).
15
+ * **Privacy-First Sanitization**: Automatically redacts sensitive fields like passwords, tokens, API keys, and cookies from request/response payloads and replays.
16
+
17
+ ---
18
+
19
+ ## Installation
20
+
21
+ Install the tracker package in your application:
22
+
23
+ ```bash
24
+ npm install @flashlog/tracker-sdk
25
+ # or
26
+ yarn add @flashlog/tracker-sdk
27
+ ```
28
+
29
+ Initialize the tracker in your application bootstrap code (e.g., `index.js` or `main.ts`):
30
+
31
+ ```typescript
32
+ import tracker from '@flashlog/tracker-sdk';
33
+
34
+ tracker.init('YOUR_PRODUCT_OR_BRAND_ID', {
35
+ apiKey: 'YOUR_PUBLIC_API_KEY',
36
+ apiUrl: 'https://api.flashlog.app/api', // optional override
37
+ enableSessionReplay: true,
38
+ });
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Configuration Options
44
+
45
+ When calling `tracker.init(brandId, config)`, you can customize behavior using the following options:
46
+
47
+ | Property | Type | Default | Description |
48
+ | :--- | :--- | :--- | :--- |
49
+ | `apiKey` | `string` | `""` | **Required.** Your Flashlog public API key. |
50
+ | `apiUrl` | `string` | `"https://api.flashlog.app/api"` | Base API endpoint for reporting. (Overridden by `VITE_FLASHLOG_API_URL` at build time if set). |
51
+ | `debug` | `boolean` | `false` | Enables verbose logging to the browser console. |
52
+ | `autoCapture` | `boolean` | `true` | Automatically configures standard global handlers for errors and rejections. |
53
+ | `captureJsErrors` | `boolean` | `true` | Captures unhandled runtime exceptions and promise rejections. |
54
+ | `captureNetworkErrors` | `boolean` | `true` | Captures failed Fetch/XHR requests (status codes $\ge 400$, network dropouts). |
55
+ | `captureSocketErrors` | `boolean` | `true` | Captures abnormal close events and connection errors on `WebSocket` connections. |
56
+ | `captureRequestBody` | `boolean` | `true` | Includes HTTP request bodies in reported network logs (redacted for privacy). |
57
+ | `captureResponseBody` | `boolean` | `true` | Includes HTTP response bodies in reported network logs (redacted for privacy). |
58
+ | `redactKeys` | `string[]` | *See defaults below* | Payload object keys to redact (replace with `[REDACTED]`). |
59
+ | `redactUrlParams` | `string[]` | *See defaults below* | Query parameters in URLs to redact (replace with `[REDACTED]`). |
60
+ | `propagateTraceHeaders` | `boolean` | `false` | Generates trace contexts and injects tracking headers into fetch/XHR requests. |
61
+ | `traceOrigins` | `string[]` | `[]` | List of allowed backend origin URL prefixes for trace header propagation. |
62
+ | `traceHeaderName` | `string` | `"x-flashlog-trace-id"` | Header name used for custom trace correlation. |
63
+ | `enableSessionReplay` | `boolean` | `true` | Enables rrweb-based video playback of user sessions. |
64
+ | `replayFlushIntervalMs`| `number` | `10000` (10s) | The time frequency in milliseconds at which recording events are flushed to storage. |
65
+ | `replayMaxSegmentBytes`| `number` | `524288` (512KB) | Maximum payload size in bytes before splitting replay segments. |
66
+ | `replayMaxSegmentEvents`| `number` | `400` | Maximum recording events count before splitting replay segments. |
67
+ | `replayMaskAllInputs` | `boolean` | `true` | Mask all text inputs/forms during session recording for user privacy. |
68
+ | `ignoredStatusCodes` | `number[]` | `[401, 403]` | HTTP response status codes that should not trigger network errors. |
69
+ | `ignoredUrls` | `string[]` | *Third-party list* | URL strings or regex patterns to ignore from error tracking (e.g. analytics). |
70
+
71
+ ---
72
+
73
+ ## Detailed Usage Guides
74
+
75
+ ### Distributing Tracing (Frontend $\rightarrow$ Backend Correlation)
76
+
77
+ To correlate frontend issues with backend execution logs:
78
+ 1. Set `propagateTraceHeaders: true` in your config.
79
+ 2. Specify your backend endpoints in `traceOrigins` (headers are blocked on external domains for security).
80
+ 3. The SDK will inject `traceparent` (W3C standard) and `x-flashlog-trace-id` (custom) headers.
81
+
82
+ ```javascript
83
+ tracker.init('YOUR_PRODUCT_ID', {
84
+ apiKey: 'YOUR_PUBLIC_API_KEY',
85
+ propagateTraceHeaders: true,
86
+ traceOrigins: ['https://api.myapp.com', 'https://staging.api.myapp.com'],
87
+ traceHeaderName: 'x-custom-trace-id' // Optional custom header override
88
+ });
89
+ ```
90
+
91
+ To fetch active trace headers for manual injection (e.g. within customized Apollo, Axios, or GraphQL clients):
92
+
93
+ ```javascript
94
+ const headers = window.FlashlogBugTracker.getTraceHeaders();
95
+ // Returns: { "traceparent": "...", "x-flashlog-trace-id": "..." }
96
+ ```
97
+
98
+ ### Identity Across Devices
99
+
100
+ You can tie browser sessions, device metadata, and recorded bugs to authenticated users:
101
+
102
+ ```javascript
103
+ // Call after login
104
+ window.FlashlogBugTracker.identify("user_db_id_987", {
105
+ email: "user@domain.com",
106
+ plan: "enterprise",
107
+ name: "John Doe"
108
+ });
109
+
110
+ // Call after logout to clear
111
+ window.FlashlogBugTracker.clearIdentity();
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Privacy & Redaction
117
+
118
+ The SDK automatically sanitizes request payloads, response bodies, and URLs to ensure PII (Personally Identifiable Information) and credentials never leave the client's browser.
119
+
120
+ ### Redacted Keys by Default:
121
+ * Credentials: `password`, `passwd`, `authorization`, `credentials`, `cookie`, `session`, `sessionToken`, `sessionCookie`, `sessionSecret`
122
+ * Security: `token`, `jwt`, `csrf`, `passcode`, `otp`, `apiKey`, `accessToken`, `refreshToken`, `clientSecret`, `privateKey`, `authCode`, `verificationCode`
123
+
124
+ To add custom keys for redaction:
125
+
126
+ ```javascript
127
+ tracker.init('YOUR_PRODUCT_ID', {
128
+ apiKey: 'YOUR_PUBLIC_API_KEY',
129
+ redactKeys: ['ssn', 'creditCardNumber', 'internalAccountNumber'],
130
+ redactUrlParams: ['token', 'inviteCode']
131
+ });
132
+ ```
133
+
134
+ ---
135
+
136
+ ## API Reference
137
+
138
+ The SDK exports the tracker instance as both the `default` export and a named export `flashlogTracker`.
139
+
140
+ ```typescript
141
+ import flashlogTracker, { flashlogTracker as namedTracker } from '@flashlog/tracker-sdk';
142
+ ```
143
+
144
+ When loaded in the browser, the instance is also attached to `window.FlashlogBugTracker` and `window.Tracker` (if the global name is not already occupied).
145
+
146
+ ### Public Methods
147
+
148
+ * **`init(brandId: string | number, config?: Partial<TrackerConfig>): Promise<void>`**
149
+ Initializes the tracking agent with your brand/product ID and optional configuration overrides.
150
+ * **`track(eventName: string, data?: Record<string, unknown>): Promise<boolean>`**
151
+ Dispatches a custom event payload to the collector backend.
152
+ * **`reportError(error: Error | string, context?: Record<string, unknown>): Promise<boolean>`**
153
+ Manually reports a handled exception/error with optional context metadata.
154
+ * **`addUserAction(action: string, details?: Record<string, unknown>): void`**
155
+ Adds a custom user action (breadcrumb) to the current session action buffer.
156
+ * **`identify(accountId: string, traits?: Record<string, unknown>): void`**
157
+ Associates the current session and subsequent reports with a specific user account.
158
+ * **`clearIdentity(): void`**
159
+ Dissociates the user identity from the tracking session.
160
+ * **`getTraceHeaders(): Record<string, string>`**
161
+ Generates and returns the active tracing headers (`traceparent` and `x-flashlog-trace-id`) for the current transaction.
162
+ * **`getSessionId(): string | null`**
163
+ Retrieves the active browser session ID.
164
+ * **`getDeviceId(): string | null`**
165
+ Retrieves the persistent device identifier.
166
+ * **`flushReplay(reason?: string): Promise<ReplayFlushResult | null>`**
167
+ Manually flushes the current recording event queue to the backend.
168
+ * **`finalizeReplay(reason?: string): Promise<ReplayFinalizeResult | null>`**
169
+ Manually finalizes the session recording and prepares it for replay playback.
170
+ * **`reset(): void`**
171
+ Resets the tracker instance, cleans local storage states, and removes global error/rejection handlers.
172
+
173
+ ---
174
+
175
+ ## License
176
+
177
+ MIT © [Eastplayers](https://github.com/Eastplayers)
@@ -0,0 +1,12 @@
1
+ import { UserAction } from "./types";
2
+ export declare class ActionBuffer {
3
+ private readonly maxSize;
4
+ private actions;
5
+ private detachHandlers;
6
+ constructor(maxSize: number);
7
+ start(): void;
8
+ stop(): void;
9
+ add(action: string, details?: Record<string, unknown>): void;
10
+ getAll(): UserAction[];
11
+ clear(): void;
12
+ }
@@ -0,0 +1,12 @@
1
+ import { TrackerConfig } from "./types";
2
+ export declare const ERROR_EVENT_NAMES: Set<string>;
3
+ export declare const DEFAULT_REDACT_KEYS: string[];
4
+ export declare const DEFAULT_REDACT_URL_PARAMS: string[];
5
+ export declare const DEFAULT_CONFIG: TrackerConfig;
6
+ export declare const STORAGE_KEYS: {
7
+ sessionId: string;
8
+ sessionSeenAt: string;
9
+ deviceId: string;
10
+ accountId: string;
11
+ accountTraits: string;
12
+ };
@@ -0,0 +1,3 @@
1
+ import { DeviceInfo, NetworkStatus } from "./types";
2
+ export declare const getDeviceInfo: (deviceId: string) => DeviceInfo;
3
+ export declare const getNetworkStatus: () => NetworkStatus;
@@ -0,0 +1,33 @@
1
+ import { TrackerConfig } from "./types";
2
+ export interface ErrorCaptureHooks {
3
+ onNetworkActivity(data: Record<string, unknown>): void;
4
+ onNetworkError(data: Record<string, unknown>): void;
5
+ onSocketError(data: Record<string, unknown>): void;
6
+ onJsError(data: Record<string, unknown>): void;
7
+ onUnhandledRejection(data: Record<string, unknown>): void;
8
+ debug: boolean;
9
+ }
10
+ export declare class ErrorCapture {
11
+ private readonly config;
12
+ private readonly hooks;
13
+ private originalFetch?;
14
+ private originalXhrOpen?;
15
+ private originalXhrSend?;
16
+ private originalWebSocket?;
17
+ private readonly socketMeta;
18
+ private initialized;
19
+ constructor(config: TrackerConfig, hooks: ErrorCaptureHooks);
20
+ start(): void;
21
+ stop(): void;
22
+ private shouldIgnoreUrl;
23
+ private shouldIgnoreStatus;
24
+ private truncate;
25
+ private getBodyType;
26
+ private safeBodyPreview;
27
+ private attachTraceHeadersToFetchRequest;
28
+ private interceptFetch;
29
+ private interceptXhr;
30
+ private interceptWebSocket;
31
+ private handleWindowError;
32
+ private handleUnhandledRejection;
33
+ }
@@ -0,0 +1,14 @@
1
+ import { DEFAULT_CONFIG } from "./constants";
2
+ import { TrackerPublicApi } from "./types";
3
+ declare global {
4
+ interface Window {
5
+ FlashlogBugTracker?: TrackerPublicApi;
6
+ Tracker?: TrackerPublicApi;
7
+ }
8
+ }
9
+ declare const tracker: TrackerPublicApi;
10
+ export declare const flashlogTracker: TrackerPublicApi;
11
+ export { DEFAULT_CONFIG };
12
+ export { createTracker, FlashlogTrackerLite } from "./tracker";
13
+ export type { DeviceInfo, NetworkStatus, ReplayFinalizeResult, ReplayFlushResult, TrackerConfig, TrackerPayload, TrackerPublicApi, TrackerTraceContext, UserAction, } from "./types";
14
+ export default tracker;
@@ -0,0 +1,10 @@
1
+ interface InstallationPingPayload {
2
+ apiKey: string;
3
+ apiUrl?: string;
4
+ brandId: number | string;
5
+ debug: boolean;
6
+ source?: "package" | "script";
7
+ scriptUrl?: string;
8
+ }
9
+ export declare const pingTrackerInstallation: (payload: InstallationPingPayload) => Promise<void>;
10
+ export {};
@@ -0,0 +1,6 @@
1
+ import { TrackerConfig, TrackerPayload } from "./types";
2
+ export declare const REDACTED_VALUE = "[redacted]";
3
+ export declare const normalizeKeySegments: (key: string) => string[];
4
+ export declare const matchesConfiguredKey: (key: string, configuredKeys: string[]) => boolean;
5
+ export declare const redactSecretLikeText: (value: string) => string;
6
+ export declare const sanitizePayload: (payload: TrackerPayload, config: TrackerConfig) => TrackerPayload;
@@ -0,0 +1,64 @@
1
+ export interface ReplayRecorderConfig {
2
+ apiKey: string;
3
+ apiUrl: string;
4
+ brandId: number;
5
+ debug: boolean;
6
+ deviceId: string | null;
7
+ maskAllInputs: boolean;
8
+ maxSegmentBytes: number;
9
+ maxSegmentEvents: number;
10
+ flushIntervalMs: number;
11
+ sessionId: string;
12
+ }
13
+ export interface ReplayFlushResult {
14
+ content_type: string;
15
+ object_key: string;
16
+ replay_session_id: string | null;
17
+ segment_id: string;
18
+ sequence_number: number;
19
+ session_id: string;
20
+ size_bytes: number;
21
+ storage_driver: string;
22
+ }
23
+ export interface ReplayFinalizeResult {
24
+ finalized_at: string;
25
+ id: string;
26
+ segment_count: number;
27
+ session_id: string;
28
+ status: string;
29
+ total_size_bytes: number;
30
+ }
31
+ export declare class ReplayRecorder {
32
+ private config;
33
+ private currentBytes;
34
+ private currentEvents;
35
+ private detachListeners;
36
+ private finalized;
37
+ private flushInFlight;
38
+ private flushTimer;
39
+ private replaySessionId;
40
+ private segmentSequence;
41
+ private stopRecording;
42
+ start(config: ReplayRecorderConfig): Promise<void>;
43
+ flush(reason?: string, options?: {
44
+ keepalive?: boolean;
45
+ }): Promise<ReplayFlushResult | null>;
46
+ finalize(reason?: string, options?: {
47
+ keepalive?: boolean;
48
+ }): Promise<ReplayFinalizeResult | null>;
49
+ dispose(options?: {
50
+ finalize?: boolean;
51
+ keepalive?: boolean;
52
+ reason?: string;
53
+ }): Promise<void>;
54
+ addCustomEvent<T>(tag: string, payload: T): void;
55
+ private bindLifecycleListeners;
56
+ private debug;
57
+ private pushEvent;
58
+ private estimateEventSize;
59
+ private nextSegmentId;
60
+ private restorePendingSegment;
61
+ private stopCapture;
62
+ private takePendingSegment;
63
+ private uploadSegment;
64
+ }
@@ -0,0 +1,10 @@
1
+ export declare const getOrCreateDeviceId: () => string;
2
+ export declare const getOrCreateSessionId: (sessionTimeoutMs: number) => string;
3
+ export declare const touchSession: () => void;
4
+ export declare const getSessionId: () => string | null;
5
+ export declare const getDeviceId: () => string | null;
6
+ export declare const setAccountIdentity: (accountId: string, traits?: Record<string, unknown>) => void;
7
+ export declare const getAccountId: () => string | null;
8
+ export declare const getAccountTraits: () => Record<string, unknown> | null;
9
+ export declare const clearAccountIdentity: () => void;
10
+ export declare const clearTrackerStorage: () => void;
@@ -0,0 +1,5 @@
1
+ import { TrackerConfig, TrackerTraceContext } from "./types";
2
+ export declare const createTraceContext: () => TrackerTraceContext;
3
+ export declare const resolveTraceContext: (data?: Record<string, unknown>) => TrackerTraceContext;
4
+ export declare const buildTraceHeaders: (traceContext: TrackerTraceContext, traceHeaderName: string) => Record<string, string>;
5
+ export declare const shouldPropagateTraceHeaders: (url: string, config: Pick<TrackerConfig, "propagateTraceHeaders" | "traceOrigins">) => boolean;
@@ -0,0 +1,45 @@
1
+ import { ReplayFinalizeResult, ReplayFlushResult, TrackerConfig, TrackerPublicApi } from "./types";
2
+ export declare class FlashlogTrackerLite implements TrackerPublicApi {
3
+ private config;
4
+ private brandId;
5
+ private sessionId;
6
+ private deviceId;
7
+ private accountId;
8
+ private accountTraits;
9
+ private readonly actionBuffer;
10
+ private errorCapture;
11
+ private readonly replayRecorder;
12
+ private initialized;
13
+ private performanceObservers;
14
+ private performanceSampleTimer;
15
+ private pendingLongTaskMs;
16
+ private resourceObserver;
17
+ private readonly replayResourceKeys;
18
+ private nextPerformanceSampleAt;
19
+ constructor();
20
+ init(brandId: string | number, config?: Partial<TrackerConfig>): Promise<void>;
21
+ track(eventName: string, data?: Record<string, unknown>): Promise<boolean>;
22
+ reportError(error: Error | string, context?: Record<string, unknown>): Promise<boolean>;
23
+ addUserAction(action: string, details?: Record<string, unknown>): void;
24
+ identify(accountId: string, traits?: Record<string, unknown>): void;
25
+ clearIdentity(): void;
26
+ getTraceHeaders(): Record<string, string>;
27
+ getSessionId(): string | null;
28
+ getDeviceId(): string | null;
29
+ flushReplay(reason?: string): Promise<ReplayFlushResult | null>;
30
+ finalizeReplay(reason?: string): Promise<ReplayFinalizeResult | null>;
31
+ reset(): void;
32
+ private buildPayload;
33
+ private trackInternal;
34
+ private recordReplaySessionContext;
35
+ private stopReplayResourceCapture;
36
+ private stopReplayPerformanceCapture;
37
+ private startReplayResourceCapture;
38
+ private startReplayPerformanceCapture;
39
+ private scheduleReplayPerformanceSample;
40
+ private captureReplayPerformanceSample;
41
+ private observeReplayLongTasks;
42
+ private observeReplayLayoutShifts;
43
+ private captureReplayResourceEntries;
44
+ }
45
+ export declare const createTracker: () => TrackerPublicApi;