@bananalytics/react-native 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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/dist/context/app.d.ts +12 -0
  4. package/dist/context/app.js +34 -0
  5. package/dist/context/device.d.ts +12 -0
  6. package/dist/context/device.js +38 -0
  7. package/dist/context/session.d.ts +49 -0
  8. package/dist/context/session.js +99 -0
  9. package/dist/core/client.d.ts +112 -0
  10. package/dist/core/client.js +283 -0
  11. package/dist/core/config.d.ts +27 -0
  12. package/dist/core/config.js +41 -0
  13. package/dist/core/errors.d.ts +19 -0
  14. package/dist/core/errors.js +40 -0
  15. package/dist/hooks/BananalyticsProvider.d.ts +22 -0
  16. package/dist/hooks/BananalyticsProvider.js +72 -0
  17. package/dist/hooks/RochadeProvider.d.ts +22 -0
  18. package/dist/hooks/RochadeProvider.js +72 -0
  19. package/dist/hooks/useBananalytics.d.ts +16 -0
  20. package/dist/hooks/useBananalytics.js +26 -0
  21. package/dist/hooks/useRochade.d.ts +16 -0
  22. package/dist/hooks/useRochade.js +26 -0
  23. package/dist/hooks/useTrackScreen.d.ts +14 -0
  24. package/dist/hooks/useTrackScreen.js +24 -0
  25. package/dist/index.d.ts +61 -0
  26. package/dist/index.js +97 -0
  27. package/dist/privacy/consent.d.ts +39 -0
  28. package/dist/privacy/consent.js +57 -0
  29. package/dist/privacy/sanitizer.d.ts +16 -0
  30. package/dist/privacy/sanitizer.js +40 -0
  31. package/dist/tracking/event-builder.d.ts +47 -0
  32. package/dist/tracking/event-builder.js +73 -0
  33. package/dist/tracking/lifecycle-tracker.d.ts +24 -0
  34. package/dist/tracking/lifecycle-tracker.js +51 -0
  35. package/dist/tracking/screen-tracker.d.ts +20 -0
  36. package/dist/tracking/screen-tracker.js +34 -0
  37. package/dist/tracking/user-identity.d.ts +40 -0
  38. package/dist/tracking/user-identity.js +75 -0
  39. package/dist/transport/batcher.d.ts +44 -0
  40. package/dist/transport/batcher.js +91 -0
  41. package/dist/transport/persister.d.ts +49 -0
  42. package/dist/transport/persister.js +148 -0
  43. package/dist/transport/queue.d.ts +41 -0
  44. package/dist/transport/queue.js +67 -0
  45. package/dist/transport/retry.d.ts +15 -0
  46. package/dist/transport/retry.js +52 -0
  47. package/dist/transport/transport.d.ts +23 -0
  48. package/dist/transport/transport.js +51 -0
  49. package/dist/types/common.d.ts +6 -0
  50. package/dist/types/common.js +2 -0
  51. package/dist/types/config.d.ts +25 -0
  52. package/dist/types/config.js +2 -0
  53. package/dist/types/events.d.ts +53 -0
  54. package/dist/types/events.js +2 -0
  55. package/dist/utils/id.d.ts +13 -0
  56. package/dist/utils/id.js +18 -0
  57. package/dist/utils/logger.d.ts +11 -0
  58. package/dist/utils/logger.js +24 -0
  59. package/dist/utils/network.d.ts +7 -0
  60. package/dist/utils/network.js +33 -0
  61. package/dist/utils/time.d.ts +13 -0
  62. package/dist/utils/time.js +17 -0
  63. package/dist/utils/validation.d.ts +26 -0
  64. package/dist/utils/validation.js +60 -0
  65. package/package.json +64 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Bananalytics
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # @bananalytics/react-native
2
+
3
+ Self-hosted analytics SDK for React Native apps. Track events, identify users, and auto-capture lifecycle data.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @bananalytics/react-native uuid
9
+ npm install @react-native-async-storage/async-storage
10
+ ```
11
+
12
+ ## Quick Start
13
+
14
+ ### Imperative API
15
+
16
+ ```typescript
17
+ import { Bananalytics } from '@bananalytics/react-native';
18
+
19
+ Bananalytics.init({
20
+ apiKey: 'rk_your_write_key',
21
+ endpoint: 'https://your-server.com',
22
+ });
23
+
24
+ Bananalytics.track('button_clicked', { button: 'signup' });
25
+ Bananalytics.identify('user-123', { plan: 'pro' });
26
+ Bananalytics.screen('HomeScreen');
27
+ ```
28
+
29
+ ### React Provider
30
+
31
+ ```tsx
32
+ import { BananalyticsProvider, useBananalytics, useTrackScreen } from '@bananalytics/react-native';
33
+
34
+ function App() {
35
+ return (
36
+ <BananalyticsProvider config={{ apiKey: 'rk_...', endpoint: 'https://...' }}>
37
+ <HomeScreen />
38
+ </BananalyticsProvider>
39
+ );
40
+ }
41
+
42
+ function HomeScreen() {
43
+ useTrackScreen('HomeScreen');
44
+ const bananalytics = useBananalytics();
45
+
46
+ return (
47
+ <Button onPress={() => bananalytics.track('button_clicked')} title="Click me" />
48
+ );
49
+ }
50
+ ```
51
+
52
+ ## Configuration
53
+
54
+ ```typescript
55
+ interface BananalyticsConfig {
56
+ apiKey: string; // Write-only public key (required)
57
+ endpoint: string; // Ingestion API URL (required)
58
+ flushInterval?: number; // ms between auto-flushes (default: 30000)
59
+ flushAt?: number; // Events before auto-flush (default: 20)
60
+ maxQueueSize?: number; // Max events in memory (default: 1000)
61
+ maxRetries?: number; // Retry attempts (default: 3)
62
+ debug?: boolean; // Enable console logging (default: false)
63
+ trackAppLifecycle?: boolean; // Auto-track foreground/background (default: true)
64
+ trackScreens?: boolean; // Auto-track screen views (default: false)
65
+ sessionTimeout?: number; // Session timeout in ms (default: 1800000)
66
+ }
67
+ ```
68
+
69
+ ## API
70
+
71
+ | Method | Description |
72
+ |---|---|
73
+ | `Bananalytics.init(config)` | Initialize the SDK |
74
+ | `Bananalytics.track(event, properties?)` | Track a custom event |
75
+ | `Bananalytics.screen(name, properties?)` | Track a screen view |
76
+ | `Bananalytics.identify(userId, traits?)` | Identify the current user |
77
+ | `Bananalytics.reset()` | Clear identity and generate new anonymous ID |
78
+ | `Bananalytics.optIn()` | Resume tracking |
79
+ | `Bananalytics.optOut()` | Stop all tracking |
80
+ | `Bananalytics.flush()` | Manually flush queued events |
81
+
82
+ ## Features
83
+
84
+ - Automatic event batching and flushing
85
+ - Offline persistence with AsyncStorage
86
+ - Exponential backoff retry on network failures
87
+ - Session tracking with configurable timeout
88
+ - Privacy controls (opt-in/opt-out)
89
+ - Zero uncaught exceptions (host app stability is sacred)
90
+
91
+ ## License
92
+
93
+ MIT
@@ -0,0 +1,12 @@
1
+ import { AppContext } from '../types/events';
2
+ /**
3
+ * Collects application information.
4
+ *
5
+ * @returns App context object
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const app = getAppContext();
10
+ * ```
11
+ */
12
+ export declare function getAppContext(): AppContext;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAppContext = getAppContext;
4
+ /**
5
+ * Collects application information.
6
+ *
7
+ * @returns App context object
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const app = getAppContext();
12
+ * ```
13
+ */
14
+ function getAppContext() {
15
+ try {
16
+ // eslint-disable-next-line @typescript-eslint/no-var-requires -- React Native module must be required at runtime
17
+ const { Platform } = require('react-native');
18
+ const constants = Platform.constants ?? {};
19
+ return {
20
+ name: constants.appName ?? 'unknown',
21
+ version: constants.appVersion ?? 'unknown',
22
+ build: constants.buildNumber ?? 'unknown',
23
+ bundleId: constants.bundleIdentifier ?? 'unknown',
24
+ };
25
+ }
26
+ catch {
27
+ return {
28
+ name: 'unknown',
29
+ version: 'unknown',
30
+ build: 'unknown',
31
+ bundleId: 'unknown',
32
+ };
33
+ }
34
+ }
@@ -0,0 +1,12 @@
1
+ import { DeviceContext } from '../types/events';
2
+ /**
3
+ * Collects device information from the React Native platform.
4
+ *
5
+ * @returns Device context object
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const device = getDeviceContext();
10
+ * ```
11
+ */
12
+ export declare function getDeviceContext(): DeviceContext;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDeviceContext = getDeviceContext;
4
+ /**
5
+ * Collects device information from the React Native platform.
6
+ *
7
+ * @returns Device context object
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const device = getDeviceContext();
12
+ * ```
13
+ */
14
+ function getDeviceContext() {
15
+ try {
16
+ // eslint-disable-next-line @typescript-eslint/no-var-requires -- React Native module must be required at runtime
17
+ const { Platform, Dimensions } = require('react-native');
18
+ const { width, height } = Dimensions.get('window');
19
+ return {
20
+ os: Platform.OS,
21
+ osVersion: String(Platform.Version),
22
+ model: Platform.constants?.Model ?? 'unknown',
23
+ manufacturer: Platform.constants?.Manufacturer ?? 'unknown',
24
+ screenWidth: width,
25
+ screenHeight: height,
26
+ };
27
+ }
28
+ catch {
29
+ return {
30
+ os: 'unknown',
31
+ osVersion: 'unknown',
32
+ model: 'unknown',
33
+ manufacturer: 'unknown',
34
+ screenWidth: 0,
35
+ screenHeight: 0,
36
+ };
37
+ }
38
+ }
@@ -0,0 +1,49 @@
1
+ import { Logger } from '../utils/logger';
2
+ import { Persister } from '../transport/persister';
3
+ /** Session state tracked across app lifecycle. */
4
+ export interface SessionState {
5
+ id: string;
6
+ startedAt: string;
7
+ lastActivity: string;
8
+ }
9
+ /**
10
+ * Manages user sessions with inactivity timeout.
11
+ * Generates session IDs and emits session start/end events.
12
+ */
13
+ export declare class SessionManager {
14
+ private session;
15
+ private readonly timeout;
16
+ private readonly logger;
17
+ private readonly persister;
18
+ private onSessionStart;
19
+ private onSessionEnd;
20
+ constructor(timeout: number, persister: Persister, logger: Logger);
21
+ /**
22
+ * Sets callbacks for session lifecycle events.
23
+ *
24
+ * @param onStart - Called when a new session starts
25
+ * @param onEnd - Called when a session ends
26
+ */
27
+ setCallbacks(onStart: (session: SessionState) => void, onEnd: (session: SessionState) => void): void;
28
+ /**
29
+ * Initializes the session manager, loading persisted state.
30
+ */
31
+ initialize(): Promise<void>;
32
+ /**
33
+ * Gets the current session, creating a new one if needed.
34
+ *
35
+ * @returns The current session state
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const session = sessionManager.getSession();
40
+ * ```
41
+ */
42
+ getSession(): SessionState;
43
+ /** Returns the current session ID, or null if no active session. */
44
+ getSessionId(): string | null;
45
+ /** Ends the current session. */
46
+ endSession(): void;
47
+ private startNewSession;
48
+ private persistSession;
49
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionManager = void 0;
4
+ const id_1 = require("../utils/id");
5
+ const time_1 = require("../utils/time");
6
+ /**
7
+ * Manages user sessions with inactivity timeout.
8
+ * Generates session IDs and emits session start/end events.
9
+ */
10
+ class SessionManager {
11
+ constructor(timeout, persister, logger) {
12
+ this.session = null;
13
+ this.onSessionStart = null;
14
+ this.onSessionEnd = null;
15
+ this.timeout = timeout;
16
+ this.persister = persister;
17
+ this.logger = logger;
18
+ }
19
+ /**
20
+ * Sets callbacks for session lifecycle events.
21
+ *
22
+ * @param onStart - Called when a new session starts
23
+ * @param onEnd - Called when a session ends
24
+ */
25
+ setCallbacks(onStart, onEnd) {
26
+ this.onSessionStart = onStart;
27
+ this.onSessionEnd = onEnd;
28
+ }
29
+ /**
30
+ * Initializes the session manager, loading persisted state.
31
+ */
32
+ async initialize() {
33
+ const persisted = await this.persister.loadSession();
34
+ if (persisted) {
35
+ this.session = persisted;
36
+ this.logger.debug('Restored session', this.session.id);
37
+ }
38
+ }
39
+ /**
40
+ * Gets the current session, creating a new one if needed.
41
+ *
42
+ * @returns The current session state
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const session = sessionManager.getSession();
47
+ * ```
48
+ */
49
+ getSession() {
50
+ const currentTime = (0, time_1.now)();
51
+ if (this.session) {
52
+ const lastActivity = new Date(this.session.lastActivity).getTime();
53
+ const elapsed = Date.now() - lastActivity;
54
+ if (elapsed > this.timeout) {
55
+ this.endSession();
56
+ return this.startNewSession(currentTime);
57
+ }
58
+ this.session.lastActivity = currentTime;
59
+ this.persistSession();
60
+ return this.session;
61
+ }
62
+ return this.startNewSession(currentTime);
63
+ }
64
+ /** Returns the current session ID, or null if no active session. */
65
+ getSessionId() {
66
+ return this.session?.id ?? null;
67
+ }
68
+ /** Ends the current session. */
69
+ endSession() {
70
+ if (this.session) {
71
+ this.logger.debug('Session ended', this.session.id);
72
+ if (this.onSessionEnd) {
73
+ this.onSessionEnd(this.session);
74
+ }
75
+ this.session = null;
76
+ }
77
+ }
78
+ startNewSession(timestamp) {
79
+ this.session = {
80
+ id: (0, id_1.generateId)(),
81
+ startedAt: timestamp,
82
+ lastActivity: timestamp,
83
+ };
84
+ this.logger.debug('New session started', this.session.id);
85
+ this.persistSession();
86
+ if (this.onSessionStart) {
87
+ this.onSessionStart(this.session);
88
+ }
89
+ return this.session;
90
+ }
91
+ persistSession() {
92
+ if (this.session) {
93
+ this.persister.saveSession(this.session).catch((err) => {
94
+ this.logger.error('Failed to persist session', err);
95
+ });
96
+ }
97
+ }
98
+ }
99
+ exports.SessionManager = SessionManager;
@@ -0,0 +1,112 @@
1
+ import { BananalyticsConfig } from '../types/config';
2
+ import { Properties } from '../types/common';
3
+ import { AsyncStorageInterface } from '../transport/persister';
4
+ import { ScreenTracker } from '../tracking/screen-tracker';
5
+ /**
6
+ * Main Bananalytics analytics client.
7
+ * Orchestrates all SDK components: tracking, transport, sessions, and privacy.
8
+ */
9
+ export declare class BananalyticsClient {
10
+ private config;
11
+ private logger;
12
+ private queue;
13
+ private transport;
14
+ private batcher;
15
+ private persister;
16
+ private identity;
17
+ private eventBuilder;
18
+ private sessionManager;
19
+ private lifecycleTracker;
20
+ private screenTracker;
21
+ private consent;
22
+ private initialized;
23
+ private deviceContext;
24
+ private appContext;
25
+ constructor(config: BananalyticsConfig, asyncStorage: AsyncStorageInterface);
26
+ /**
27
+ * Initializes the SDK — loads persisted state and starts auto-tracking.
28
+ * Must be called before any tracking methods.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const client = new BananalyticsClient(config, AsyncStorage);
33
+ * await client.initialize();
34
+ * ```
35
+ */
36
+ initialize(): Promise<void>;
37
+ /**
38
+ * Tracks a custom event.
39
+ *
40
+ * @param eventName - The name of the event
41
+ * @param properties - Optional event properties
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * client.track('button_clicked', { button: 'signup' });
46
+ * ```
47
+ */
48
+ track(eventName: string, properties?: Properties): void;
49
+ /**
50
+ * Tracks a screen view event.
51
+ *
52
+ * @param screenName - The name of the screen
53
+ * @param properties - Optional screen properties
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * client.screen('HomeScreen');
58
+ * ```
59
+ */
60
+ screen(screenName: string, properties?: Properties): void;
61
+ /**
62
+ * Identifies the current user.
63
+ *
64
+ * @param userId - The user identifier
65
+ * @param traits - Optional user traits
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * client.identify('user-123', { plan: 'pro' });
70
+ * ```
71
+ */
72
+ identify(userId: string, traits?: Properties): void;
73
+ /**
74
+ * Clears user identity, generates a new anonymous ID, and clears the queue.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * client.reset();
79
+ * ```
80
+ */
81
+ reset(): void;
82
+ /**
83
+ * Opts the user into analytics tracking.
84
+ */
85
+ optIn(): void;
86
+ /**
87
+ * Opts the user out of analytics tracking. Stops all event collection.
88
+ */
89
+ optOut(): void;
90
+ /**
91
+ * Manually flushes all queued events to the backend.
92
+ *
93
+ * @returns Promise that resolves when the flush completes
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * await client.flush();
98
+ * ```
99
+ */
100
+ flush(): Promise<void>;
101
+ /**
102
+ * Shuts down the SDK — stops auto-tracking and flushes remaining events.
103
+ */
104
+ shutdown(): Promise<void>;
105
+ /** Returns the screen tracker for React Navigation integration. */
106
+ getScreenTracker(): ScreenTracker;
107
+ private getContext;
108
+ private enqueueEvent;
109
+ private persistQueue;
110
+ private getLocale;
111
+ private getTimezone;
112
+ }