screen-flow 1.0.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,126 @@
1
+ # screen-flow 🧭
2
+
3
+ > **Navigation intelligence, not just analytics.** One-stop, platform-agnostic tracker for React and React Native.
4
+
5
+ [![license](https://img.shields.io/badge/license-ISC-blue.svg)](https://github.com/your-username/screen-flow/blob/main/LICENSE)
6
+ [![size](https://img.shields.io/bundlephobia/minzip/screen-flow)](https://bundlephobia.com/package/screen-flow)
7
+ [![tests](https://img.shields.io/badge/tests-90%25%2B-brightgreen)](https://github.com/your-username/screen-flow)
8
+
9
+ ---
10
+
11
+ ## πŸš€ Why ScreenFlow?
12
+
13
+ In a world of bloated analytics SDKs (Segment, Firebase, Mixpanel), **ScreenFlow** is the minimalist's choice.
14
+
15
+ - **⚑ Zero Bloat:** Only a few KB. Won't slow down your app or increase your bundle size like 2MB+ commercial SDKs.
16
+ - **πŸ›‘οΈ Privacy First:** No data ever leaves the device unless *you* want it to. Total control over your user's privacy.
17
+ - **πŸ“± True Cross-Platform:** Write once, track everywhere. Same API for Web and React Native with automatic lifecycle detection.
18
+ - **🧠 More than Counters:** Automatically tracks **duration**, maintains **history**, and detects **back-navigation** using smart algorithms.
19
+ - **πŸ’Ύ Persistence Ready:** Unlike other lightweight trackers, ScreenFlow can persist data across app restarts.
20
+ - **πŸ§ͺ 100% Transparent:** Open-source and fully covered by unit tests. No "black-box" tracking.
21
+
22
+ ---
23
+
24
+ ## ✨ Features
25
+
26
+ - πŸ”„ **Flow Tracking**: Keeps track of the last 30 screens visited.
27
+ - ⏱️ **Time Awareness**: Automatically tracks time spent on each screen.
28
+ - πŸ”™ **Smart Back Detection**: Detects back navigation even across multiple screens.
29
+ - ⏸️ **Smart Pausing**: Pauses timers when the app is in background.
30
+ - βš›οΈ **React Ready**: Comes with first-class hooks like `useScreenFlow`.
31
+ - πŸ”Œ **Adapter Pattern**: Send data to Console, Firestore, Segment, or your own API.
32
+
33
+ ---
34
+
35
+ ## πŸš€ Installation
36
+
37
+ ```bash
38
+ npm install screen-flow
39
+ ```
40
+
41
+ ---
42
+
43
+ ## βš™οΈ Quick Start
44
+
45
+ ### 1. Initialize (Optional but recommended)
46
+
47
+ Setup your storage and output adapter.
48
+
49
+ ```typescript
50
+ import { initScreenFlow, ConsoleAdapter } from 'screen-flow';
51
+
52
+ initScreenFlow({
53
+ adapter: new ConsoleAdapter(),
54
+ // Persistence for Web (localStorage) or React Native (AsyncStorage)
55
+ storage: localStorage,
56
+ sessionTimeout: 30 * 60 * 1000 // 30 mins
57
+ });
58
+ ```
59
+
60
+ ### 2. Track Screens
61
+
62
+ #### βš›οΈ Functional Components (React)
63
+
64
+ ```tsx
65
+ import { useScreenFlow } from 'screen-flow';
66
+
67
+ const Dashboard = () => {
68
+ useScreenFlow('Dashboard', { tab: 'overview' });
69
+ return <div>My Dashboard</div>;
70
+ };
71
+ ```
72
+
73
+ #### πŸ–±οΈ Manual Tracking
74
+
75
+ ```typescript
76
+ import { onScreenChange } from 'screen-flow';
77
+
78
+ // Anywhere in your logic
79
+ await onScreenChange('Settings');
80
+ ```
81
+
82
+ ---
83
+
84
+ ## πŸ”Œ Customizing Output (Adapters)
85
+
86
+ Connect ScreenFlow to any service in seconds.
87
+
88
+ ```typescript
89
+ import { Adapter, ScreenEvent } from 'screen-flow';
90
+
91
+ class MyBrandAdapter implements Adapter {
92
+ onEvent(event: ScreenEvent) {
93
+ console.log(`User stayed on ${event.previousScreen} for ${event.duration}ms`);
94
+ // Send to your own server
95
+ myApi.log(event);
96
+ }
97
+ }
98
+ ```
99
+
100
+ ---
101
+
102
+ ## πŸ“„ Screen Event Data Structure
103
+
104
+ | Property | Type | Description |
105
+ | :--- | :--- | :--- |
106
+ | `screen` | `string` | Current screen name |
107
+ | `previousScreen` | `string \| null` | Name of the last screen |
108
+ | `duration` | `number` | Time spent on the last screen (ms) |
109
+ | `flow` | `string[]` | Last 30 screens visited |
110
+ | `isBack` | `boolean` | True if this move was a "Back" navigation |
111
+ | `sessionId` | `string` | Unique, persistent session ID |
112
+ | `params` | `object` | Custom metadata provided by you |
113
+
114
+ ---
115
+
116
+ ## πŸ’– Support
117
+
118
+ If ScreenFlow saved you hours of work or helped you build a faster app, consider supporting the development!
119
+
120
+ <a href="https://www.buymeacoffee.com/yourusername" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
121
+
122
+ ---
123
+
124
+ ## πŸ›‘οΈ License
125
+
126
+ ISC (Free and Open Source)
@@ -0,0 +1,7 @@
1
+ import { Adapter, ScreenEvent } from '../types';
2
+ /**
3
+ * Default adapter that logs screen events to the console.
4
+ */
5
+ export declare class ConsoleAdapter implements Adapter {
6
+ onEvent(event: ScreenEvent): void;
7
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConsoleAdapter = void 0;
4
+ /**
5
+ * Default adapter that logs screen events to the console.
6
+ */
7
+ class ConsoleAdapter {
8
+ onEvent(event) {
9
+ const { screen, duration, isBack, timestamp } = event;
10
+ const date = new Date(timestamp).toLocaleTimeString();
11
+ console.log(`[ScreenFlow] ${date} - ${screen} (${duration}ms)${isBack ? ' [BACK]' : ''}`, event);
12
+ }
13
+ }
14
+ exports.ConsoleAdapter = ConsoleAdapter;
@@ -0,0 +1,17 @@
1
+ import { StorageProvider } from '../types';
2
+ /**
3
+ * Manages the navigation history stack.
4
+ * Keeps track of the last 30 screens and detects back navigation.
5
+ */
6
+ export declare class FlowManager {
7
+ private history;
8
+ private storage?;
9
+ private readonly MAX_HISTORY;
10
+ private readonly STORAGE_KEY;
11
+ constructor(storage?: StorageProvider);
12
+ private init;
13
+ push(screenName: string): Promise<boolean>;
14
+ getHistory(): string[];
15
+ getPreviousScreen(): string | null;
16
+ private isBackNavigation;
17
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlowManager = void 0;
4
+ /**
5
+ * Manages the navigation history stack.
6
+ * Keeps track of the last 30 screens and detects back navigation.
7
+ */
8
+ class FlowManager {
9
+ constructor(storage) {
10
+ this.history = [];
11
+ this.MAX_HISTORY = 30;
12
+ this.STORAGE_KEY = 'sf_history';
13
+ this.storage = storage;
14
+ this.init();
15
+ }
16
+ async init() {
17
+ if (this.storage) {
18
+ const storedHistory = await this.storage.getItem(this.STORAGE_KEY);
19
+ if (storedHistory) {
20
+ try {
21
+ this.history = JSON.parse(storedHistory);
22
+ }
23
+ catch (e) {
24
+ this.history = [];
25
+ }
26
+ }
27
+ }
28
+ }
29
+ async push(screenName) {
30
+ const isBack = this.isBackNavigation(screenName);
31
+ this.history.push(screenName);
32
+ if (this.history.length > this.MAX_HISTORY) {
33
+ this.history.shift();
34
+ }
35
+ if (this.storage) {
36
+ await this.storage.setItem(this.STORAGE_KEY, JSON.stringify(this.history));
37
+ }
38
+ return isBack;
39
+ }
40
+ getHistory() {
41
+ return [...this.history];
42
+ }
43
+ getPreviousScreen() {
44
+ if (this.history.length < 2)
45
+ return null;
46
+ return this.history[this.history.length - 2];
47
+ }
48
+ isBackNavigation(screenName) {
49
+ if (this.history.length < 2)
50
+ return false;
51
+ // Smarter back detection: if the screen exists in the recent history
52
+ // (last 5 screens excluding the current one)
53
+ const recentHistory = this.history.slice(-6, -1);
54
+ return recentHistory.includes(screenName);
55
+ }
56
+ }
57
+ exports.FlowManager = FlowManager;
@@ -0,0 +1,16 @@
1
+ import { StorageProvider } from '../types';
2
+ /**
3
+ * Handles session identification.
4
+ * Generates a unique ID per session, persisted across restarts.
5
+ */
6
+ export declare class SessionManager {
7
+ private sessionId;
8
+ private storage?;
9
+ private readonly STORAGE_KEY;
10
+ private readonly LAST_ACTIVITY_KEY;
11
+ private readonly sessionTimeout;
12
+ constructor(storage?: StorageProvider, sessionTimeout?: number);
13
+ getSessionId(): Promise<string>;
14
+ private updateActivity;
15
+ private generateId;
16
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionManager = void 0;
4
+ /**
5
+ * Handles session identification.
6
+ * Generates a unique ID per session, persisted across restarts.
7
+ */
8
+ class SessionManager {
9
+ constructor(storage, sessionTimeout = 30 * 60 * 1000) {
10
+ this.sessionId = null;
11
+ this.STORAGE_KEY = 'sf_session_id';
12
+ this.LAST_ACTIVITY_KEY = 'sf_last_activity';
13
+ this.storage = storage;
14
+ this.sessionTimeout = sessionTimeout;
15
+ }
16
+ async getSessionId() {
17
+ if (this.sessionId) {
18
+ await this.updateActivity();
19
+ return this.sessionId;
20
+ }
21
+ if (this.storage) {
22
+ const storedId = await this.storage.getItem(this.STORAGE_KEY);
23
+ const lastActivity = await this.storage.getItem(this.LAST_ACTIVITY_KEY);
24
+ if (storedId && lastActivity) {
25
+ const elapsed = Date.now() - parseInt(lastActivity, 10);
26
+ if (elapsed < this.sessionTimeout) {
27
+ this.sessionId = storedId;
28
+ await this.updateActivity();
29
+ return this.sessionId;
30
+ }
31
+ }
32
+ }
33
+ // New session
34
+ this.sessionId = this.generateId();
35
+ if (this.storage) {
36
+ await this.storage.setItem(this.STORAGE_KEY, this.sessionId);
37
+ await this.updateActivity();
38
+ }
39
+ return this.sessionId;
40
+ }
41
+ async updateActivity() {
42
+ if (this.storage) {
43
+ await this.storage.setItem(this.LAST_ACTIVITY_KEY, Date.now().toString());
44
+ }
45
+ }
46
+ generateId() {
47
+ return Math.random().toString(36).substring(2, 11) + Date.now().toString(36);
48
+ }
49
+ }
50
+ exports.SessionManager = SessionManager;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Handles duration tracking for screens.
3
+ * Automatically pauses when app is backgrounded.
4
+ */
5
+ export declare class ScreenTimer {
6
+ private startTime;
7
+ private pausedTime;
8
+ private isPaused;
9
+ private lastPauseStart;
10
+ constructor();
11
+ start(): void;
12
+ pause(): void;
13
+ resume(): void;
14
+ getDuration(): number;
15
+ reset(): number;
16
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScreenTimer = void 0;
4
+ /**
5
+ * Handles duration tracking for screens.
6
+ * Automatically pauses when app is backgrounded.
7
+ */
8
+ class ScreenTimer {
9
+ constructor() {
10
+ this.startTime = 0;
11
+ this.pausedTime = 0;
12
+ this.isPaused = false;
13
+ this.lastPauseStart = 0;
14
+ this.start();
15
+ }
16
+ start() {
17
+ this.startTime = Date.now();
18
+ this.pausedTime = 0;
19
+ this.isPaused = false;
20
+ }
21
+ pause() {
22
+ if (!this.isPaused) {
23
+ this.lastPauseStart = Date.now();
24
+ this.isPaused = true;
25
+ }
26
+ }
27
+ resume() {
28
+ if (this.isPaused) {
29
+ this.pausedTime += Date.now() - this.lastPauseStart;
30
+ this.isPaused = false;
31
+ }
32
+ }
33
+ getDuration() {
34
+ if (this.startTime === 0)
35
+ return 0;
36
+ const currentDuration = Date.now() - this.startTime - this.pausedTime;
37
+ // If currently paused, don't count the time since pause started
38
+ if (this.isPaused) {
39
+ return currentDuration - (Date.now() - this.lastPauseStart);
40
+ }
41
+ return currentDuration;
42
+ }
43
+ reset() {
44
+ const duration = this.getDuration();
45
+ this.start();
46
+ return duration;
47
+ }
48
+ }
49
+ exports.ScreenTimer = ScreenTimer;
@@ -0,0 +1,32 @@
1
+ import { TrackerOptions } from '../types';
2
+ /**
3
+ * Main coordinator for screen tracking.
4
+ * Manages timer, flow, and session across the application lifecycle.
5
+ */
6
+ export declare class ScreenTracker {
7
+ private timer;
8
+ private flow;
9
+ private session;
10
+ private adapter;
11
+ private currentScreen;
12
+ constructor(options?: TrackerOptions);
13
+ /**
14
+ * Tracks a screen change.
15
+ * @param screenName The name of the screen being navigated to.
16
+ * @param params Optional parameters associated with the screen change.
17
+ */
18
+ track(screenName: string, params?: Record<string, any>): Promise<void>;
19
+ /**
20
+ * Returns the current session ID.
21
+ */
22
+ getSessionId(): Promise<string>;
23
+ /**
24
+ * Returns the navigation history.
25
+ */
26
+ getHistory(): string[];
27
+ /**
28
+ * Clears the tracker state (useful for tests).
29
+ */
30
+ clearInstance(): void;
31
+ }
32
+ export declare const tracker: ScreenTracker;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tracker = exports.ScreenTracker = void 0;
4
+ const timer_1 = require("./timer");
5
+ const flow_1 = require("./flow");
6
+ const session_1 = require("./session");
7
+ const appState_1 = require("../lifecycle/appState");
8
+ const console_1 = require("../adapters/console");
9
+ /**
10
+ * Main coordinator for screen tracking.
11
+ * Manages timer, flow, and session across the application lifecycle.
12
+ */
13
+ class ScreenTracker {
14
+ constructor(options = {}) {
15
+ this.currentScreen = null;
16
+ this.timer = new timer_1.ScreenTimer();
17
+ this.flow = new flow_1.FlowManager(options.storage);
18
+ this.session = new session_1.SessionManager(options.storage, options.sessionTimeout);
19
+ this.adapter = options.adapter || new console_1.ConsoleAdapter();
20
+ // Setup platform-agnostic lifecycle listener
21
+ new appState_1.AppStateListener((status) => {
22
+ if (status === 'background') {
23
+ this.timer.pause();
24
+ }
25
+ else {
26
+ this.timer.resume();
27
+ }
28
+ });
29
+ }
30
+ /**
31
+ * Tracks a screen change.
32
+ * @param screenName The name of the screen being navigated to.
33
+ * @param params Optional parameters associated with the screen change.
34
+ */
35
+ async track(screenName, params) {
36
+ if (this.currentScreen === screenName)
37
+ return;
38
+ const previousScreen = this.currentScreen;
39
+ this.currentScreen = screenName;
40
+ // Reset timer and get duration spent on the previous screen
41
+ const duration = this.timer.reset();
42
+ // Update flow and detect if this is a back navigation
43
+ const isBack = await this.flow.push(screenName);
44
+ // Prepare the event
45
+ const event = {
46
+ screen: screenName,
47
+ previousScreen,
48
+ duration,
49
+ flow: this.flow.getHistory(),
50
+ isBack,
51
+ sessionId: await this.session.getSessionId(),
52
+ timestamp: Date.now(),
53
+ params
54
+ };
55
+ // Dispatch event to the adapter
56
+ this.adapter.onEvent(event);
57
+ }
58
+ /**
59
+ * Returns the current session ID.
60
+ */
61
+ async getSessionId() {
62
+ return await this.session.getSessionId();
63
+ }
64
+ /**
65
+ * Returns the navigation history.
66
+ */
67
+ getHistory() {
68
+ return this.flow.getHistory();
69
+ }
70
+ /**
71
+ * Clears the tracker state (useful for tests).
72
+ */
73
+ clearInstance() {
74
+ this.currentScreen = null;
75
+ this.timer.start();
76
+ this.flow = new flow_1.FlowManager(this.storage);
77
+ this.session = new session_1.SessionManager(this.storage);
78
+ }
79
+ }
80
+ exports.ScreenTracker = ScreenTracker;
81
+ // Export a singleton instance for easier usage
82
+ exports.tracker = new ScreenTracker();
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Hook to automatically track screen changes in React components.
3
+ *
4
+ * @param screenName The name of the screen to track.
5
+ * @param params Optional parameters for the screen.
6
+ */
7
+ export declare function useScreenFlow(screenName: string, params?: Record<string, any>): void;
package/dist/hooks.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useScreenFlow = useScreenFlow;
4
+ const react_1 = require("react");
5
+ const index_1 = require("./index");
6
+ /**
7
+ * Hook to automatically track screen changes in React components.
8
+ *
9
+ * @param screenName The name of the screen to track.
10
+ * @param params Optional parameters for the screen.
11
+ */
12
+ function useScreenFlow(screenName, params) {
13
+ const isFirstRender = (0, react_1.useRef)(true);
14
+ (0, react_1.useEffect)(() => {
15
+ // Track on mount or whenever screenName/params change
16
+ (0, index_1.onScreenChange)(screenName, params);
17
+ isFirstRender.current = false;
18
+ }, [screenName, JSON.stringify(params)]);
19
+ }
@@ -0,0 +1,21 @@
1
+ export * from './types';
2
+ export { ScreenTracker, tracker } from './core/tracker';
3
+ export { ConsoleAdapter } from './adapters/console';
4
+ import { tracker } from './core/tracker';
5
+ import { TrackerOptions } from './types';
6
+ /**
7
+ * Initializes the ScreenTracker with custom options (adapter, storage).
8
+ */
9
+ export declare const initScreenFlow: (options: TrackerOptions) => void;
10
+ /**
11
+ * Tracks a screen change.
12
+ */
13
+ export declare const onScreenChange: (screenName: string, params?: Record<string, any>) => Promise<void>;
14
+ export * from './hooks';
15
+ /**
16
+ * Main API for screen tracking.
17
+ * @example
18
+ * import screenFlow from 'screen-flow';
19
+ * await screenFlow.track('Home');
20
+ */
21
+ export default tracker;
package/dist/index.js ADDED
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.onScreenChange = exports.initScreenFlow = exports.ConsoleAdapter = exports.tracker = exports.ScreenTracker = void 0;
18
+ __exportStar(require("./types"), exports);
19
+ var tracker_1 = require("./core/tracker");
20
+ Object.defineProperty(exports, "ScreenTracker", { enumerable: true, get: function () { return tracker_1.ScreenTracker; } });
21
+ Object.defineProperty(exports, "tracker", { enumerable: true, get: function () { return tracker_1.tracker; } });
22
+ var console_1 = require("./adapters/console");
23
+ Object.defineProperty(exports, "ConsoleAdapter", { enumerable: true, get: function () { return console_1.ConsoleAdapter; } });
24
+ const tracker_2 = require("./core/tracker");
25
+ /**
26
+ * Initializes the ScreenTracker with custom options (adapter, storage).
27
+ */
28
+ const initScreenFlow = (options) => {
29
+ tracker_2.tracker.adapter = options.adapter || tracker_2.tracker.adapter;
30
+ // Update internal managers with storage/options
31
+ if (options.storage) {
32
+ tracker_2.tracker.flow['storage'] = options.storage;
33
+ tracker_2.tracker.session['storage'] = options.storage;
34
+ }
35
+ if (options.sessionTimeout) {
36
+ tracker_2.tracker.session['sessionTimeout'] = options.sessionTimeout;
37
+ }
38
+ };
39
+ exports.initScreenFlow = initScreenFlow;
40
+ /**
41
+ * Tracks a screen change.
42
+ */
43
+ const onScreenChange = async (screenName, params) => {
44
+ await tracker_2.tracker.track(screenName, params);
45
+ };
46
+ exports.onScreenChange = onScreenChange;
47
+ __exportStar(require("./hooks"), exports);
48
+ /**
49
+ * Main API for screen tracking.
50
+ * @example
51
+ * import screenFlow from 'screen-flow';
52
+ * await screenFlow.track('Home');
53
+ */
54
+ exports.default = tracker_2.tracker;
@@ -0,0 +1,10 @@
1
+ import { AppStateChangeListener } from '../types';
2
+ /**
3
+ * Platform-agnostic AppState listener.
4
+ * Automatically detects React Native vs Web.
5
+ */
6
+ export declare class AppStateListener {
7
+ private listener;
8
+ constructor(listener: AppStateChangeListener);
9
+ private init;
10
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AppStateListener = void 0;
4
+ /**
5
+ * Platform-agnostic AppState listener.
6
+ * Automatically detects React Native vs Web.
7
+ */
8
+ class AppStateListener {
9
+ constructor(listener) {
10
+ this.listener = listener;
11
+ this.init();
12
+ }
13
+ init() {
14
+ // Check if we are in React Native
15
+ try {
16
+ // Use dynamic require to avoid bundling RN in web builds
17
+ const { AppState } = require('react-native');
18
+ if (AppState && AppState.addEventListener) {
19
+ AppState.addEventListener('change', (nextState) => {
20
+ if (nextState === 'active') {
21
+ this.listener('active');
22
+ }
23
+ else if (nextState === 'background' || nextState === 'inactive') {
24
+ this.listener('background');
25
+ }
26
+ });
27
+ return;
28
+ }
29
+ }
30
+ catch (e) {
31
+ // React Native not available, fallback to Web
32
+ }
33
+ // Fallback to Web (document visibility API)
34
+ if (typeof document !== 'undefined' && document.addEventListener) {
35
+ document.addEventListener('visibilitychange', () => {
36
+ const status = document.visibilityState === 'visible' ? 'active' : 'background';
37
+ this.listener(status);
38
+ });
39
+ }
40
+ }
41
+ }
42
+ exports.AppStateListener = AppStateListener;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Represents a single screen event in the navigation flow.
3
+ */
4
+ export interface ScreenEvent {
5
+ screen: string;
6
+ previousScreen: string | null;
7
+ duration: number;
8
+ flow: string[];
9
+ isBack: boolean;
10
+ sessionId: string;
11
+ timestamp: number;
12
+ params?: Record<string, any>;
13
+ }
14
+ /**
15
+ * Interface for output adapters.
16
+ */
17
+ export interface Adapter {
18
+ onEvent(event: ScreenEvent): void;
19
+ }
20
+ /**
21
+ * Interface for storage providers (persistence).
22
+ */
23
+ export interface StorageProvider {
24
+ getItem(key: string): string | null | Promise<string | null>;
25
+ setItem(key: string, value: string): void | Promise<void>;
26
+ removeItem(key: string): void | Promise<void>;
27
+ }
28
+ /**
29
+ * Options for initializing the ScreenTracker.
30
+ */
31
+ export interface TrackerOptions {
32
+ adapter?: Adapter;
33
+ storage?: StorageProvider;
34
+ sessionTimeout?: number;
35
+ }
36
+ /**
37
+ * App state types to unify React and React Native behavior.
38
+ */
39
+ export type AppStateStatus = 'active' | 'background';
40
+ export type AppStateChangeListener = (status: AppStateStatus) => void;
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "screen-flow",
3
+ "version": "1.0.0",
4
+ "description": "Navigation intelligence library for React and React Native",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepublishOnly": "npm run build",
10
+ "test": "jest",
11
+ "test:watch": "jest --watch"
12
+ },
13
+ "keywords": [
14
+ "react",
15
+ "react-native",
16
+ "navigation",
17
+ "analytics",
18
+ "flow",
19
+ "screen-tracking"
20
+ ],
21
+ "author": "Mansi Kansagara (https://github.com/Mansikansagara9)",
22
+ "license": "ISC",
23
+ "files": [
24
+ "dist",
25
+ "README.md"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/Mansikansagara9/screen-flow.git"
30
+ },
31
+ "contributors": [
32
+ {
33
+ "name": "Kaushik Vaghasiya",
34
+ "email": "kaushikvaghasiya134@gmail.com",
35
+ "url": "https://github.com/kaushik134"
36
+ }
37
+ ],
38
+ "devDependencies": {
39
+ "@types/jest": "^30.0.0",
40
+ "@types/react": "^19.2.7",
41
+ "jest": "^29.7.0",
42
+ "ts-jest": "^29.4.6",
43
+ "typescript": "^5.0.0"
44
+ },
45
+ "peerDependencies": {
46
+ "react": ">=16.8.0",
47
+ "react-native": ">=0.60.0"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "react-native": {
51
+ "optional": true
52
+ }
53
+ }
54
+ }