nova-hooks 2.0.3 → 2.0.8
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/dist/index.cjs.js +66 -12
- package/dist/index.es.js +66 -12
- package/dist/src/event-bus.d.ts +7 -0
- package/dist/src/hooks.d.ts +15 -9
- package/dist/src/socket.d.ts +16 -3
- package/package.json +52 -52
- package/readme.md +146 -146
package/dist/index.cjs.js
CHANGED
|
@@ -2,11 +2,23 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
let socket_io_client = require("socket.io-client");
|
|
3
3
|
let react = require("react");
|
|
4
4
|
//#region src/socket.ts
|
|
5
|
+
/**
|
|
6
|
+
* Singleton instance of the Socket.IO client.
|
|
7
|
+
* Kept global to ensure only one connection exists per application lifecycle.
|
|
8
|
+
*/
|
|
5
9
|
var socket;
|
|
10
|
+
/**
|
|
11
|
+
* The global identifier for the current application/project.
|
|
12
|
+
* Attached to all emitted events to segregate data in the backend dashboard.
|
|
13
|
+
*/
|
|
6
14
|
var projectName;
|
|
7
15
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
16
|
+
* Initializes and establishes a real-time connection to the Nova socket server.
|
|
17
|
+
* This should typically be called once at the root of the application (e.g., App.tsx).
|
|
18
|
+
*
|
|
19
|
+
* @param socketUrl The absolute URL of the socket server to connect to
|
|
20
|
+
* @param project The unique identifier or name of the consuming application
|
|
21
|
+
* @throws {Error} If the socketUrl or project name is missing or empty
|
|
10
22
|
*/
|
|
11
23
|
var connectSocket = (socketUrl, project) => {
|
|
12
24
|
if (!socketUrl || socketUrl.trim() === "") throw new Error("Socket URL is required to connect.");
|
|
@@ -22,7 +34,8 @@ var connectSocket = (socketUrl, project) => {
|
|
|
22
34
|
});
|
|
23
35
|
};
|
|
24
36
|
/**
|
|
25
|
-
*
|
|
37
|
+
* Gracefully disconnects the active socket connection and completely
|
|
38
|
+
* destroys the local instance, freeing up resources.
|
|
26
39
|
*/
|
|
27
40
|
var disconnectSocket = () => {
|
|
28
41
|
if (socket) {
|
|
@@ -32,27 +45,53 @@ var disconnectSocket = () => {
|
|
|
32
45
|
};
|
|
33
46
|
//#endregion
|
|
34
47
|
//#region src/event-bus.ts
|
|
48
|
+
/**
|
|
49
|
+
* A lightweight custom event emitter implementation.
|
|
50
|
+
* Created to avoid bundling the bulky Node.js 'events' package.
|
|
51
|
+
*/
|
|
35
52
|
var SimpleEventEmitter = class {
|
|
36
53
|
listeners = {};
|
|
54
|
+
/**
|
|
55
|
+
* Registers a listener callback for a specific event.
|
|
56
|
+
* @param event The name of the event to listen for
|
|
57
|
+
* @param callback The function to execute when the event is emitted
|
|
58
|
+
*/
|
|
37
59
|
on(event, callback) {
|
|
38
60
|
if (!this.listeners[event]) this.listeners[event] = [];
|
|
39
61
|
this.listeners[event].push(callback);
|
|
40
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Emits an event, triggering all registered listeners synchronously.
|
|
65
|
+
* @param event The name of the event to emit
|
|
66
|
+
* @param data The payload to pass to the listener callbacks
|
|
67
|
+
*/
|
|
41
68
|
emit(event, data) {
|
|
42
69
|
if (this.listeners[event]) this.listeners[event].forEach((cb) => cb(data));
|
|
43
70
|
}
|
|
44
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* Predefined set of UI element types that can be tracked.
|
|
74
|
+
* Used to categorize standard user click interactions.
|
|
75
|
+
*/
|
|
45
76
|
var ActionType = {
|
|
46
77
|
Button: "Button",
|
|
47
78
|
Menu: "Menu",
|
|
48
79
|
Login: "Login",
|
|
49
80
|
Link: "Link"
|
|
50
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* Constant identifier representing a page dwell time tracking event.
|
|
84
|
+
*/
|
|
51
85
|
var PageVisitAction = "Page Visit";
|
|
86
|
+
/** Internal event identifier used for routing payloads within the EventBus */
|
|
52
87
|
var APP_EVENT = "APP_EVENT";
|
|
88
|
+
/** The singleton instance of the event bus */
|
|
53
89
|
var eventBus = new SimpleEventEmitter();
|
|
90
|
+
/** The socket event name used when streaming data to the backend */
|
|
54
91
|
var NOVA_USER_ACTIVITY_EVENT = "nova-user-activity";
|
|
92
|
+
/** Map storing aggregated, debounced click events keyed by their unique signature */
|
|
55
93
|
var pendingClicks = /* @__PURE__ */ new Map();
|
|
94
|
+
/** Reference to the active batching debounce timer */
|
|
56
95
|
var batchTimer = null;
|
|
57
96
|
/**
|
|
58
97
|
* Event listener for APP_EVENT that processes and enriches event data
|
|
@@ -104,6 +143,12 @@ function withEvent(eventData, callback) {
|
|
|
104
143
|
}
|
|
105
144
|
//#endregion
|
|
106
145
|
//#region src/hooks.ts
|
|
146
|
+
/**
|
|
147
|
+
* Helper utility to generate a unique CSS selector path for an HTML element.
|
|
148
|
+
* Used as a fallback identifier when a clicked element lacks semantic text or explicit tracking IDs.
|
|
149
|
+
* @param el The HTML element to generate a path for
|
|
150
|
+
* @returns A string representing the CSS selector path (e.g., 'div.container > button#submit')
|
|
151
|
+
*/
|
|
107
152
|
var getCssPath = (el) => {
|
|
108
153
|
const path = [];
|
|
109
154
|
let current = el;
|
|
@@ -120,10 +165,13 @@ var getCssPath = (el) => {
|
|
|
120
165
|
return path.join(" > ");
|
|
121
166
|
};
|
|
122
167
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
168
|
+
* React hook to track global click events and automatically emit them to the server.
|
|
169
|
+
* Attaches a single event listener to the document (event delegation) to capture clicks.
|
|
170
|
+
* It intelligently identifies interactive elements (buttons, links, pointer cursors) and
|
|
171
|
+
* extracts their labels or generates a CSS path fallback if no label is found.
|
|
172
|
+
*
|
|
173
|
+
* @param empId Optional employee ID for tracking identity
|
|
174
|
+
* @param roleId Optional role ID for tracking authorization/role
|
|
127
175
|
*/
|
|
128
176
|
var useGlobalClickTracker = (empId, roleId) => {
|
|
129
177
|
(0, react.useEffect)(() => {
|
|
@@ -165,13 +213,19 @@ var useGlobalClickTracker = (empId, roleId) => {
|
|
|
165
213
|
};
|
|
166
214
|
}, [empId, roleId]);
|
|
167
215
|
};
|
|
216
|
+
/**
|
|
217
|
+
* Minimum active dwell time (in seconds) required before a page visit event is considered valid and emitted.
|
|
218
|
+
*/
|
|
168
219
|
var DURATION_THRESHOLD = 5;
|
|
169
220
|
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
221
|
+
* React hook to track accurate active page visit duration.
|
|
222
|
+
* Utilizes the native Page Visibility API to pause the timer when the user switches tabs
|
|
223
|
+
* or minimizes the browser, ensuring only actual active dwell time is recorded.
|
|
224
|
+
* Emits the payload automatically when the component unmounts.
|
|
225
|
+
*
|
|
226
|
+
* @param action The specific action name or page identifier for the visit
|
|
227
|
+
* @param empId Optional employee ID for tracking identity
|
|
228
|
+
* @param empRole Optional employee role for tracking authorization/role
|
|
175
229
|
*/
|
|
176
230
|
var usePageTimeTracker = (action, empId, empRole) => {
|
|
177
231
|
const activeTimeRef = (0, react.useRef)(0);
|
package/dist/index.es.js
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import { io } from "socket.io-client";
|
|
2
2
|
import { useEffect, useRef } from "react";
|
|
3
3
|
//#region src/socket.ts
|
|
4
|
+
/**
|
|
5
|
+
* Singleton instance of the Socket.IO client.
|
|
6
|
+
* Kept global to ensure only one connection exists per application lifecycle.
|
|
7
|
+
*/
|
|
4
8
|
var socket;
|
|
9
|
+
/**
|
|
10
|
+
* The global identifier for the current application/project.
|
|
11
|
+
* Attached to all emitted events to segregate data in the backend dashboard.
|
|
12
|
+
*/
|
|
5
13
|
var projectName;
|
|
6
14
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
15
|
+
* Initializes and establishes a real-time connection to the Nova socket server.
|
|
16
|
+
* This should typically be called once at the root of the application (e.g., App.tsx).
|
|
17
|
+
*
|
|
18
|
+
* @param socketUrl The absolute URL of the socket server to connect to
|
|
19
|
+
* @param project The unique identifier or name of the consuming application
|
|
20
|
+
* @throws {Error} If the socketUrl or project name is missing or empty
|
|
9
21
|
*/
|
|
10
22
|
var connectSocket = (socketUrl, project) => {
|
|
11
23
|
if (!socketUrl || socketUrl.trim() === "") throw new Error("Socket URL is required to connect.");
|
|
@@ -21,7 +33,8 @@ var connectSocket = (socketUrl, project) => {
|
|
|
21
33
|
});
|
|
22
34
|
};
|
|
23
35
|
/**
|
|
24
|
-
*
|
|
36
|
+
* Gracefully disconnects the active socket connection and completely
|
|
37
|
+
* destroys the local instance, freeing up resources.
|
|
25
38
|
*/
|
|
26
39
|
var disconnectSocket = () => {
|
|
27
40
|
if (socket) {
|
|
@@ -31,27 +44,53 @@ var disconnectSocket = () => {
|
|
|
31
44
|
};
|
|
32
45
|
//#endregion
|
|
33
46
|
//#region src/event-bus.ts
|
|
47
|
+
/**
|
|
48
|
+
* A lightweight custom event emitter implementation.
|
|
49
|
+
* Created to avoid bundling the bulky Node.js 'events' package.
|
|
50
|
+
*/
|
|
34
51
|
var SimpleEventEmitter = class {
|
|
35
52
|
listeners = {};
|
|
53
|
+
/**
|
|
54
|
+
* Registers a listener callback for a specific event.
|
|
55
|
+
* @param event The name of the event to listen for
|
|
56
|
+
* @param callback The function to execute when the event is emitted
|
|
57
|
+
*/
|
|
36
58
|
on(event, callback) {
|
|
37
59
|
if (!this.listeners[event]) this.listeners[event] = [];
|
|
38
60
|
this.listeners[event].push(callback);
|
|
39
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Emits an event, triggering all registered listeners synchronously.
|
|
64
|
+
* @param event The name of the event to emit
|
|
65
|
+
* @param data The payload to pass to the listener callbacks
|
|
66
|
+
*/
|
|
40
67
|
emit(event, data) {
|
|
41
68
|
if (this.listeners[event]) this.listeners[event].forEach((cb) => cb(data));
|
|
42
69
|
}
|
|
43
70
|
};
|
|
71
|
+
/**
|
|
72
|
+
* Predefined set of UI element types that can be tracked.
|
|
73
|
+
* Used to categorize standard user click interactions.
|
|
74
|
+
*/
|
|
44
75
|
var ActionType = {
|
|
45
76
|
Button: "Button",
|
|
46
77
|
Menu: "Menu",
|
|
47
78
|
Login: "Login",
|
|
48
79
|
Link: "Link"
|
|
49
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Constant identifier representing a page dwell time tracking event.
|
|
83
|
+
*/
|
|
50
84
|
var PageVisitAction = "Page Visit";
|
|
85
|
+
/** Internal event identifier used for routing payloads within the EventBus */
|
|
51
86
|
var APP_EVENT = "APP_EVENT";
|
|
87
|
+
/** The singleton instance of the event bus */
|
|
52
88
|
var eventBus = new SimpleEventEmitter();
|
|
89
|
+
/** The socket event name used when streaming data to the backend */
|
|
53
90
|
var NOVA_USER_ACTIVITY_EVENT = "nova-user-activity";
|
|
91
|
+
/** Map storing aggregated, debounced click events keyed by their unique signature */
|
|
54
92
|
var pendingClicks = /* @__PURE__ */ new Map();
|
|
93
|
+
/** Reference to the active batching debounce timer */
|
|
55
94
|
var batchTimer = null;
|
|
56
95
|
/**
|
|
57
96
|
* Event listener for APP_EVENT that processes and enriches event data
|
|
@@ -103,6 +142,12 @@ function withEvent(eventData, callback) {
|
|
|
103
142
|
}
|
|
104
143
|
//#endregion
|
|
105
144
|
//#region src/hooks.ts
|
|
145
|
+
/**
|
|
146
|
+
* Helper utility to generate a unique CSS selector path for an HTML element.
|
|
147
|
+
* Used as a fallback identifier when a clicked element lacks semantic text or explicit tracking IDs.
|
|
148
|
+
* @param el The HTML element to generate a path for
|
|
149
|
+
* @returns A string representing the CSS selector path (e.g., 'div.container > button#submit')
|
|
150
|
+
*/
|
|
106
151
|
var getCssPath = (el) => {
|
|
107
152
|
const path = [];
|
|
108
153
|
let current = el;
|
|
@@ -119,10 +164,13 @@ var getCssPath = (el) => {
|
|
|
119
164
|
return path.join(" > ");
|
|
120
165
|
};
|
|
121
166
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
167
|
+
* React hook to track global click events and automatically emit them to the server.
|
|
168
|
+
* Attaches a single event listener to the document (event delegation) to capture clicks.
|
|
169
|
+
* It intelligently identifies interactive elements (buttons, links, pointer cursors) and
|
|
170
|
+
* extracts their labels or generates a CSS path fallback if no label is found.
|
|
171
|
+
*
|
|
172
|
+
* @param empId Optional employee ID for tracking identity
|
|
173
|
+
* @param roleId Optional role ID for tracking authorization/role
|
|
126
174
|
*/
|
|
127
175
|
var useGlobalClickTracker = (empId, roleId) => {
|
|
128
176
|
useEffect(() => {
|
|
@@ -164,13 +212,19 @@ var useGlobalClickTracker = (empId, roleId) => {
|
|
|
164
212
|
};
|
|
165
213
|
}, [empId, roleId]);
|
|
166
214
|
};
|
|
215
|
+
/**
|
|
216
|
+
* Minimum active dwell time (in seconds) required before a page visit event is considered valid and emitted.
|
|
217
|
+
*/
|
|
167
218
|
var DURATION_THRESHOLD = 5;
|
|
168
219
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
220
|
+
* React hook to track accurate active page visit duration.
|
|
221
|
+
* Utilizes the native Page Visibility API to pause the timer when the user switches tabs
|
|
222
|
+
* or minimizes the browser, ensuring only actual active dwell time is recorded.
|
|
223
|
+
* Emits the payload automatically when the component unmounts.
|
|
224
|
+
*
|
|
225
|
+
* @param action The specific action name or page identifier for the visit
|
|
226
|
+
* @param empId Optional employee ID for tracking identity
|
|
227
|
+
* @param empRole Optional employee role for tracking authorization/role
|
|
174
228
|
*/
|
|
175
229
|
var usePageTimeTracker = (action, empId, empRole) => {
|
|
176
230
|
const activeTimeRef = useRef(0);
|
package/dist/src/event-bus.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Predefined set of UI element types that can be tracked.
|
|
3
|
+
* Used to categorize standard user click interactions.
|
|
4
|
+
*/
|
|
1
5
|
export declare const ActionType: {
|
|
2
6
|
readonly Button: "Button";
|
|
3
7
|
readonly Menu: "Menu";
|
|
4
8
|
readonly Login: "Login";
|
|
5
9
|
readonly Link: "Link";
|
|
6
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* Constant identifier representing a page dwell time tracking event.
|
|
13
|
+
*/
|
|
7
14
|
export declare const PageVisitAction = "Page Visit";
|
|
8
15
|
/**
|
|
9
16
|
* Represents the structure of event data that can be emitted
|
package/dist/src/hooks.d.ts
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { EventData } from './event-bus';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* React hook to track global click events and automatically emit them to the server.
|
|
4
|
+
* Attaches a single event listener to the document (event delegation) to capture clicks.
|
|
5
|
+
* It intelligently identifies interactive elements (buttons, links, pointer cursors) and
|
|
6
|
+
* extracts their labels or generates a CSS path fallback if no label is found.
|
|
7
|
+
*
|
|
8
|
+
* @param empId Optional employee ID for tracking identity
|
|
9
|
+
* @param roleId Optional role ID for tracking authorization/role
|
|
7
10
|
*/
|
|
8
11
|
declare const useGlobalClickTracker: (empId?: string, roleId?: string) => void;
|
|
9
12
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* React hook to track accurate active page visit duration.
|
|
14
|
+
* Utilizes the native Page Visibility API to pause the timer when the user switches tabs
|
|
15
|
+
* or minimizes the browser, ensuring only actual active dwell time is recorded.
|
|
16
|
+
* Emits the payload automatically when the component unmounts.
|
|
17
|
+
*
|
|
18
|
+
* @param action The specific action name or page identifier for the visit
|
|
19
|
+
* @param empId Optional employee ID for tracking identity
|
|
20
|
+
* @param empRole Optional employee role for tracking authorization/role
|
|
15
21
|
*/
|
|
16
22
|
declare const usePageTimeTracker: (action: EventData["Action"], empId?: EventData["EmpId"], empRole?: EventData["EmpRole"]) => void;
|
|
17
23
|
export { useGlobalClickTracker, usePageTimeTracker };
|
package/dist/src/socket.d.ts
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
import { io } from 'socket.io-client';
|
|
2
|
+
/**
|
|
3
|
+
* Singleton instance of the Socket.IO client.
|
|
4
|
+
* Kept global to ensure only one connection exists per application lifecycle.
|
|
5
|
+
*/
|
|
2
6
|
export declare let socket: ReturnType<typeof io> | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* The global identifier for the current application/project.
|
|
9
|
+
* Attached to all emitted events to segregate data in the backend dashboard.
|
|
10
|
+
*/
|
|
3
11
|
export declare let projectName: string | number;
|
|
4
12
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
13
|
+
* Initializes and establishes a real-time connection to the Nova socket server.
|
|
14
|
+
* This should typically be called once at the root of the application (e.g., App.tsx).
|
|
15
|
+
*
|
|
16
|
+
* @param socketUrl The absolute URL of the socket server to connect to
|
|
17
|
+
* @param project The unique identifier or name of the consuming application
|
|
18
|
+
* @throws {Error} If the socketUrl or project name is missing or empty
|
|
7
19
|
*/
|
|
8
20
|
export declare const connectSocket: (socketUrl: string, project: string) => void;
|
|
9
21
|
/**
|
|
10
|
-
*
|
|
22
|
+
* Gracefully disconnects the active socket connection and completely
|
|
23
|
+
* destroys the local instance, freeing up resources.
|
|
11
24
|
*/
|
|
12
25
|
export declare const disconnectSocket: () => void;
|
package/package.json
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "nova-hooks",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "Nova hooks package",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"nova",
|
|
7
|
-
"hooks"
|
|
8
|
-
],
|
|
9
|
-
"homepage": "https://github.com/mjjabarullah/nova-hooks#readme",
|
|
10
|
-
"bugs": {
|
|
11
|
-
"url": "https://github.com/mjjabarullah/nova-hooks/issues"
|
|
12
|
-
},
|
|
13
|
-
"repository": {
|
|
14
|
-
"type": "git",
|
|
15
|
-
"url": "git+https://github.com/mjjabarullah/nova-hooks.git"
|
|
16
|
-
},
|
|
17
|
-
"license": "MIT",
|
|
18
|
-
"author": "Mohamed Jabarullah",
|
|
19
|
-
"type": "module",
|
|
20
|
-
"main": "dist/index.cjs.js",
|
|
21
|
-
"module": "dist/index.es.js",
|
|
22
|
-
"types": "dist/index.d.ts",
|
|
23
|
-
"files": [
|
|
24
|
-
"dist"
|
|
25
|
-
],
|
|
26
|
-
"scripts": {
|
|
27
|
-
"build": "vite build",
|
|
28
|
-
"prepare": "vite build",
|
|
29
|
-
"test": "vitest run",
|
|
30
|
-
"test:watch": "vitest",
|
|
31
|
-
"coverage": "vitest run --coverage"
|
|
32
|
-
},
|
|
33
|
-
"dependencies": {
|
|
34
|
-
"socket.io-client": "^4.8.3"
|
|
35
|
-
},
|
|
36
|
-
"peerDependencies": {
|
|
37
|
-
"react": "^18.0.0 || ^19.0.0"
|
|
38
|
-
},
|
|
39
|
-
"devDependencies": {
|
|
40
|
-
"@testing-library/jest-dom": "^6.9.1",
|
|
41
|
-
"@testing-library/react": "^16.3.2",
|
|
42
|
-
"@types/node": "^25.6.2",
|
|
43
|
-
"@types/react": "^19.2.14",
|
|
44
|
-
"@types/react-dom": "^19.2.3",
|
|
45
|
-
"@vitest/coverage-v8": "^3.2.4",
|
|
46
|
-
"jsdom": "^27.0.1",
|
|
47
|
-
"typescript": "~6.0.3",
|
|
48
|
-
"vite": "^8.0.12",
|
|
49
|
-
"vite-plugin-dts": "^5.0.0",
|
|
50
|
-
"vitest": "^3.2.4"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "nova-hooks",
|
|
3
|
+
"version": "2.0.8",
|
|
4
|
+
"description": "Nova hooks package",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nova",
|
|
7
|
+
"hooks"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://github.com/mjjabarullah/nova-hooks#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/mjjabarullah/nova-hooks/issues"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/mjjabarullah/nova-hooks.git"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "Mohamed Jabarullah",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "dist/index.cjs.js",
|
|
21
|
+
"module": "dist/index.es.js",
|
|
22
|
+
"types": "dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "vite build",
|
|
28
|
+
"prepare": "vite build",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"coverage": "vitest run --coverage"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"socket.io-client": "^4.8.3"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
41
|
+
"@testing-library/react": "^16.3.2",
|
|
42
|
+
"@types/node": "^25.6.2",
|
|
43
|
+
"@types/react": "^19.2.14",
|
|
44
|
+
"@types/react-dom": "^19.2.3",
|
|
45
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
46
|
+
"jsdom": "^27.0.1",
|
|
47
|
+
"typescript": "~6.0.3",
|
|
48
|
+
"vite": "^8.0.12",
|
|
49
|
+
"vite-plugin-dts": "^5.0.0",
|
|
50
|
+
"vitest": "^3.2.4"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/readme.md
CHANGED
|
@@ -1,146 +1,146 @@
|
|
|
1
|
-
# nova-hooks
|
|
2
|
-
|
|
3
|
-
A highly-optimized, **zero-configuration** event tracking utility for capturing user interactions and page visits in React applications. Built with a custom, lightweight `EventBus` and `socket.io-client`.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## ✨ Features
|
|
8
|
-
|
|
9
|
-
- **Zero-Config Tracking:** Automatically captures clicks on buttons, links, and interactive elements without needing manual attributes!
|
|
10
|
-
- **True Page Dwell Time:** Uses the native Page Visibility API to calculate the _actual_ active time spent on a page (pauses when the user switches tabs).
|
|
11
|
-
- **Network Optimized:** Employs a built-in 1-second debouncing and click-batching mechanism to minimize network overhead.
|
|
12
|
-
- **Ultra-Lightweight:** Zero dependencies (other than `socket.io-client`). Fully framework-agnostic core logic.
|
|
13
|
-
- **Auto-Enriched Context:** Automatically attaches `PageUrl`, `PageTitle`, `Project`, and `CreatedDate` to every single event payload.
|
|
14
|
-
- **SSR Safe:** Fully compatible with Next.js and Remix.
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## 🚀 Getting Started
|
|
19
|
-
|
|
20
|
-
### 1. Install
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
yarn add nova-hooks
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npm i nova-hooks
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### 2. Initialization
|
|
31
|
-
|
|
32
|
-
> [!IMPORTANT]
|
|
33
|
-
> Ensure the socket connection is established at the root of your application using the `connectSocket` method.
|
|
34
|
-
|
|
35
|
-
```tsx
|
|
36
|
-
import { connectSocket } from "nova-hooks";
|
|
37
|
-
import { useEffect } from "react";
|
|
38
|
-
|
|
39
|
-
const App = () => {
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
connectSocket(import.meta.env.VITE_APP_SOCKET, "WolfPack");
|
|
42
|
-
}, []);
|
|
43
|
-
|
|
44
|
-
// app code...
|
|
45
|
-
};
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## 🛠️ Usage Examples
|
|
51
|
-
|
|
52
|
-
### 1. Zero-Config Global Click Tracking
|
|
53
|
-
|
|
54
|
-
Place `useGlobalClickTracker` in your authenticated layout. It will automatically listen to the DOM and seamlessly track clicks on any `<button>`, `<a>`, or `cursor: pointer` element!
|
|
55
|
-
|
|
56
|
-
```tsx
|
|
57
|
-
import { useGlobalClickTracker } from "nova-hooks";
|
|
58
|
-
|
|
59
|
-
const ProtectedLayout = ({ children }) => {
|
|
60
|
-
const { user } = useAuth();
|
|
61
|
-
|
|
62
|
-
// Magic happens here! Tracks all UI clicks across the entire app.
|
|
63
|
-
useGlobalClickTracker(user?.empId, user?.roleId);
|
|
64
|
-
|
|
65
|
-
return <>{children}</>;
|
|
66
|
-
};
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
_Note: It will intelligently extract labels from `innerText`, `title`, `aria-label`, or auto-generate a fallback CSS Path!_
|
|
70
|
-
|
|
71
|
-
### 2. Explicit Tracking (Optional Overrides)
|
|
72
|
-
|
|
73
|
-
If you want to override the auto-detected name for a specific button, just attach the `data-nova-*` attributes:
|
|
74
|
-
|
|
75
|
-
```tsx
|
|
76
|
-
<button
|
|
77
|
-
data-nova-track-id="Custom Task Action"
|
|
78
|
-
data-nova-track-type={ActionType.Button}
|
|
79
|
-
>
|
|
80
|
-
Track Me Specifically
|
|
81
|
-
</button>
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### 3. Accurate Page Dwell Time
|
|
85
|
-
|
|
86
|
-
Track exactly how long a user actively stares at a specific page/component. The timer pauses if they switch tabs or minimize the browser!
|
|
87
|
-
|
|
88
|
-
```tsx
|
|
89
|
-
import { usePageTimeTracker } from "nova-hooks";
|
|
90
|
-
|
|
91
|
-
export const Dashboard = () => {
|
|
92
|
-
const { user } = useAuth();
|
|
93
|
-
|
|
94
|
-
// Tracks active dwell time and emits it automatically when the user leaves the page
|
|
95
|
-
usePageTimeTracker("Home Dashboard", user?.empId, user?.roleId);
|
|
96
|
-
|
|
97
|
-
return <div>Welcome to the Dashboard</div>;
|
|
98
|
-
};
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### 4. Manual / Imperative Tracking
|
|
102
|
-
|
|
103
|
-
If you need to track events programmatically (like inside an API success callback), use the `withEvent` method:
|
|
104
|
-
|
|
105
|
-
```tsx
|
|
106
|
-
import { withEvent, ActionType } from "nova-hooks";
|
|
107
|
-
|
|
108
|
-
const doLogin = () => {
|
|
109
|
-
api.login().then(() => {
|
|
110
|
-
withEvent({
|
|
111
|
-
Action: "Login Success",
|
|
112
|
-
ActionType: ActionType.Login,
|
|
113
|
-
EmpId: "EMP-123",
|
|
114
|
-
EmpRole: "Admin",
|
|
115
|
-
Count: 1,
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
};
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## 📦 Exports & Types
|
|
124
|
-
|
|
125
|
-
| Name | Type | Description |
|
|
126
|
-
| ----------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------- |
|
|
127
|
-
| `ActionType` | `object` | Predefined enum-like values: `"Button"`, `"Menu"`, `"Login"`, `"Link"` |
|
|
128
|
-
| `PageVisitAction` | `string` | Constant string `"Page Visit"` used for tracking page visits |
|
|
129
|
-
| `connectSocket` | `(socketUrl: string, project: string) => void` | Connects to the socket server and sets the project name. |
|
|
130
|
-
| `disconnectSocket` | `() => void` | Disconnects and cleans up the active socket connection. |
|
|
131
|
-
| `withEvent` | `(eventData: EventData, callback?: Function) => void` | Emits a structured event optionally after running a callback. |
|
|
132
|
-
| `useGlobalClickTracker` | `(empId?: string, roleId?: string) => void` | React hook to automatically track global click events. |
|
|
133
|
-
| `usePageTimeTracker` | `(action: string; empId?: string; empRole?: string) => void` | React hook to track active time spent on a page via the Visibility API. |
|
|
134
|
-
|
|
135
|
-
### EventData Structure
|
|
136
|
-
|
|
137
|
-
All events are automatically enriched with `Project`, `PageUrl`, `PageTitle`, and an ISO `CreatedDate`.
|
|
138
|
-
|
|
139
|
-
| Property | Type | Description |
|
|
140
|
-
| ------------ | ----------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
141
|
-
| `ActionType` | `keyof typeof ActionType` \| `typeof PageVisitAction` | Type of the action. |
|
|
142
|
-
| `Action` | `string` | Specific action performed (extracted from DOM or manually set). |
|
|
143
|
-
| `EmpId` | `string` | Unique identifier of the employee. |
|
|
144
|
-
| `EmpRole` | `string` | Role of the employee performing the action. |
|
|
145
|
-
| `Count` | `number` _(only when `ActionType` is from `ActionType`)_ | Number of rapid occurrences of the action (batched). |
|
|
146
|
-
| `Duration` | `number` _(seconds, only when `ActionType` is `PageVisitAction`)_ | Duration of the _active_ visit in seconds. |
|
|
1
|
+
# nova-hooks
|
|
2
|
+
|
|
3
|
+
A highly-optimized, **zero-configuration** event tracking utility for capturing user interactions and page visits in React applications. Built with a custom, lightweight `EventBus` and `socket.io-client`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## ✨ Features
|
|
8
|
+
|
|
9
|
+
- **Zero-Config Tracking:** Automatically captures clicks on buttons, links, and interactive elements without needing manual attributes!
|
|
10
|
+
- **True Page Dwell Time:** Uses the native Page Visibility API to calculate the _actual_ active time spent on a page (pauses when the user switches tabs).
|
|
11
|
+
- **Network Optimized:** Employs a built-in 1-second debouncing and click-batching mechanism to minimize network overhead.
|
|
12
|
+
- **Ultra-Lightweight:** Zero dependencies (other than `socket.io-client`). Fully framework-agnostic core logic.
|
|
13
|
+
- **Auto-Enriched Context:** Automatically attaches `PageUrl`, `PageTitle`, `Project`, and `CreatedDate` to every single event payload.
|
|
14
|
+
- **SSR Safe:** Fully compatible with Next.js and Remix.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🚀 Getting Started
|
|
19
|
+
|
|
20
|
+
### 1. Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
yarn add nova-hooks
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm i nova-hooks
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. Initialization
|
|
31
|
+
|
|
32
|
+
> [!IMPORTANT]
|
|
33
|
+
> Ensure the socket connection is established at the root of your application using the `connectSocket` method.
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { connectSocket } from "nova-hooks";
|
|
37
|
+
import { useEffect } from "react";
|
|
38
|
+
|
|
39
|
+
const App = () => {
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
connectSocket(import.meta.env.VITE_APP_SOCKET, "WolfPack");
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
// app code...
|
|
45
|
+
};
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 🛠️ Usage Examples
|
|
51
|
+
|
|
52
|
+
### 1. Zero-Config Global Click Tracking
|
|
53
|
+
|
|
54
|
+
Place `useGlobalClickTracker` in your authenticated layout. It will automatically listen to the DOM and seamlessly track clicks on any `<button>`, `<a>`, or `cursor: pointer` element!
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { useGlobalClickTracker } from "nova-hooks";
|
|
58
|
+
|
|
59
|
+
const ProtectedLayout = ({ children }) => {
|
|
60
|
+
const { user } = useAuth();
|
|
61
|
+
|
|
62
|
+
// Magic happens here! Tracks all UI clicks across the entire app.
|
|
63
|
+
useGlobalClickTracker(user?.empId, user?.roleId);
|
|
64
|
+
|
|
65
|
+
return <>{children}</>;
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
_Note: It will intelligently extract labels from `innerText`, `title`, `aria-label`, or auto-generate a fallback CSS Path!_
|
|
70
|
+
|
|
71
|
+
### 2. Explicit Tracking (Optional Overrides)
|
|
72
|
+
|
|
73
|
+
If you want to override the auto-detected name for a specific button, just attach the `data-nova-*` attributes:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<button
|
|
77
|
+
data-nova-track-id="Custom Task Action"
|
|
78
|
+
data-nova-track-type={ActionType.Button}
|
|
79
|
+
>
|
|
80
|
+
Track Me Specifically
|
|
81
|
+
</button>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Accurate Page Dwell Time
|
|
85
|
+
|
|
86
|
+
Track exactly how long a user actively stares at a specific page/component. The timer pauses if they switch tabs or minimize the browser!
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { usePageTimeTracker } from "nova-hooks";
|
|
90
|
+
|
|
91
|
+
export const Dashboard = () => {
|
|
92
|
+
const { user } = useAuth();
|
|
93
|
+
|
|
94
|
+
// Tracks active dwell time and emits it automatically when the user leaves the page
|
|
95
|
+
usePageTimeTracker("Home Dashboard", user?.empId, user?.roleId);
|
|
96
|
+
|
|
97
|
+
return <div>Welcome to the Dashboard</div>;
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 4. Manual / Imperative Tracking
|
|
102
|
+
|
|
103
|
+
If you need to track events programmatically (like inside an API success callback), use the `withEvent` method:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
import { withEvent, ActionType } from "nova-hooks";
|
|
107
|
+
|
|
108
|
+
const doLogin = () => {
|
|
109
|
+
api.login().then(() => {
|
|
110
|
+
withEvent({
|
|
111
|
+
Action: "Login Success",
|
|
112
|
+
ActionType: ActionType.Login,
|
|
113
|
+
EmpId: "EMP-123",
|
|
114
|
+
EmpRole: "Admin",
|
|
115
|
+
Count: 1,
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 📦 Exports & Types
|
|
124
|
+
|
|
125
|
+
| Name | Type | Description |
|
|
126
|
+
| ----------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------- |
|
|
127
|
+
| `ActionType` | `object` | Predefined enum-like values: `"Button"`, `"Menu"`, `"Login"`, `"Link"` |
|
|
128
|
+
| `PageVisitAction` | `string` | Constant string `"Page Visit"` used for tracking page visits |
|
|
129
|
+
| `connectSocket` | `(socketUrl: string, project: string) => void` | Connects to the socket server and sets the project name. |
|
|
130
|
+
| `disconnectSocket` | `() => void` | Disconnects and cleans up the active socket connection. |
|
|
131
|
+
| `withEvent` | `(eventData: EventData, callback?: Function) => void` | Emits a structured event optionally after running a callback. |
|
|
132
|
+
| `useGlobalClickTracker` | `(empId?: string, roleId?: string) => void` | React hook to automatically track global click events. |
|
|
133
|
+
| `usePageTimeTracker` | `(action: string; empId?: string; empRole?: string) => void` | React hook to track active time spent on a page via the Visibility API. |
|
|
134
|
+
|
|
135
|
+
### EventData Structure
|
|
136
|
+
|
|
137
|
+
All events are automatically enriched with `Project`, `PageUrl`, `PageTitle`, and an ISO `CreatedDate`.
|
|
138
|
+
|
|
139
|
+
| Property | Type | Description |
|
|
140
|
+
| ------------ | ----------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
141
|
+
| `ActionType` | `keyof typeof ActionType` \| `typeof PageVisitAction` | Type of the action. |
|
|
142
|
+
| `Action` | `string` | Specific action performed (extracted from DOM or manually set). |
|
|
143
|
+
| `EmpId` | `string` | Unique identifier of the employee. |
|
|
144
|
+
| `EmpRole` | `string` | Role of the employee performing the action. |
|
|
145
|
+
| `Count` | `number` _(only when `ActionType` is from `ActionType`)_ | Number of rapid occurrences of the action (batched). |
|
|
146
|
+
| `Duration` | `number` _(seconds, only when `ActionType` is `PageVisitAction`)_ | Duration of the _active_ visit in seconds. |
|