teacupnet-lib 1.0.0 → 1.1.1
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/Analytics/data.controller.js +57 -1
- package/Analytics/identity.js +25 -0
- package/Analytics/save.js +19 -6
- package/CHANGELOG.md +24 -0
- package/Functions/FetchAPI.js +6 -3
- package/package.json +1 -1
- package/Analytics/fingerprint.js +0 -10
|
@@ -3,6 +3,62 @@ const data = {
|
|
|
3
3
|
form: [],
|
|
4
4
|
page: [],
|
|
5
5
|
};
|
|
6
|
+
const prevData = {
|
|
7
|
+
button: [],
|
|
8
|
+
form: [],
|
|
9
|
+
page: [],
|
|
10
|
+
};
|
|
11
|
+
const clearData = () => {
|
|
12
|
+
prevData.button = [...data.button];
|
|
13
|
+
prevData.form = [...data.form];
|
|
14
|
+
prevData.page = [...data.page];
|
|
15
|
+
data.button = [];
|
|
16
|
+
data.form = [];
|
|
17
|
+
data.page = [];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const getNewData = () => {
|
|
21
|
+
const newData = {
|
|
22
|
+
button: [],
|
|
23
|
+
form: [],
|
|
24
|
+
page: [],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Compare buttons - only include if not in prevData
|
|
28
|
+
data.button.forEach((btn) => {
|
|
29
|
+
const isDuplicate = prevData.button.some(
|
|
30
|
+
(prevBtn) => prevBtn.button === btn.button && prevBtn.page === btn.page
|
|
31
|
+
);
|
|
32
|
+
if (!isDuplicate) {
|
|
33
|
+
newData.button.push(btn);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Compare pages - only include if page+percentage combo not in prevData
|
|
38
|
+
data.page.forEach((pg) => {
|
|
39
|
+
const isDuplicate = prevData.page.some(
|
|
40
|
+
(prevPg) =>
|
|
41
|
+
prevPg.page === pg.page && prevPg.percentage === pg.percentage
|
|
42
|
+
);
|
|
43
|
+
if (!isDuplicate) {
|
|
44
|
+
newData.page.push(pg);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Compare forms - only include if form+percent combo not in prevData
|
|
49
|
+
data.form.forEach((frm) => {
|
|
50
|
+
const isDuplicate = prevData.form.some(
|
|
51
|
+
(prevFrm) =>
|
|
52
|
+
prevFrm.form === frm.form && prevFrm.percent === frm.percent
|
|
53
|
+
);
|
|
54
|
+
if (!isDuplicate) {
|
|
55
|
+
newData.form.push(frm);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return newData;
|
|
60
|
+
};
|
|
61
|
+
|
|
6
62
|
const setData = (type, result) => {
|
|
7
63
|
if (type === 'page') {
|
|
8
64
|
// Find existing page entry
|
|
@@ -50,4 +106,4 @@ const setData = (type, result) => {
|
|
|
50
106
|
return data;
|
|
51
107
|
};
|
|
52
108
|
|
|
53
|
-
export default { setData, data };
|
|
109
|
+
export default { setData, data, clearData, getNewData };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const FINGERPRINT_KEY = 'teacupnet_fingerprint';
|
|
2
|
+
|
|
3
|
+
export default function fingerprint() {
|
|
4
|
+
// Check if fingerprint already exists in localStorage
|
|
5
|
+
const existingFingerprint = localStorage.getItem(FINGERPRINT_KEY);
|
|
6
|
+
|
|
7
|
+
if (existingFingerprint) {
|
|
8
|
+
return existingFingerprint;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Generate new fingerprint if not found
|
|
12
|
+
const canvas = document.createElement('canvas');
|
|
13
|
+
canvas.width = 20;
|
|
14
|
+
canvas.height = 2220;
|
|
15
|
+
const ctx = canvas.getContext('2d');
|
|
16
|
+
ctx.fillText('Hello', 10, 10);
|
|
17
|
+
|
|
18
|
+
const data = canvas.toDataURL();
|
|
19
|
+
const newFingerprint = data.slice(-32);
|
|
20
|
+
|
|
21
|
+
// Store in localStorage for future use
|
|
22
|
+
localStorage.setItem(FINGERPRINT_KEY, newFingerprint);
|
|
23
|
+
|
|
24
|
+
return newFingerprint;
|
|
25
|
+
}
|
package/Analytics/save.js
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
import fetchAPI from '../Functions/FetchAPI';
|
|
2
|
-
import fingerprint from './
|
|
2
|
+
import fingerprint from './identity';
|
|
3
3
|
import data from './data.controller';
|
|
4
4
|
import { initial } from '..';
|
|
5
5
|
|
|
6
6
|
export default function save() {
|
|
7
7
|
setInterval(() => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
const newData = data.getNewData();
|
|
9
|
+
|
|
10
|
+
// Check if there's any new data to send
|
|
11
|
+
const hasNewData =
|
|
12
|
+
newData.button.length > 0 ||
|
|
13
|
+
newData.form.length > 0 ||
|
|
14
|
+
newData.page.length > 0;
|
|
15
|
+
|
|
16
|
+
if (hasNewData) {
|
|
17
|
+
fetchAPI('api/analytics', 'POST', {
|
|
18
|
+
data: newData,
|
|
19
|
+
initial: initial,
|
|
20
|
+
fingerprint: fingerprint(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Clear data after sending
|
|
24
|
+
data.clearData();
|
|
25
|
+
}
|
|
13
26
|
}, 5000);
|
|
14
27
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
|
|
9
|
+
## [1.1.0] - 2025-12-26
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Duplicate Prevention System**: Analytics data is now compared with previously sent data to prevent duplicates from being stored in the database
|
|
13
|
+
- **LocalStorage Fingerprint**: Visitor fingerprint now persists in localStorage across browser sessions for consistent identification
|
|
14
|
+
- `getNewData()` function in data controller that filters out duplicate analytics before sending
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Fingerprint generation now happens only once per browser and is stored in localStorage with key `teacupnet_fingerprint`
|
|
18
|
+
- Analytics save function now only sends API requests when there is new data to transmit
|
|
19
|
+
- Data comparison logic ensures button clicks, page views, and form interactions are only sent once
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- Duplicate button clicks on the same page are no longer sent multiple times
|
|
23
|
+
- Same page scroll percentages are no longer duplicated in database
|
|
24
|
+
- Form completion percentages only update when they change
|
|
25
|
+
- Reduced unnecessary API calls when no new analytics data is available
|
|
26
|
+
|
|
27
|
+
### Performance
|
|
28
|
+
- Reduced database writes by filtering duplicates before transmission
|
|
29
|
+
- Reduced network requests by skipping empty data intervals
|
|
30
|
+
- Fingerprint generation optimized to run only once per browser session
|
|
31
|
+
|
|
8
32
|
## [1.0.0] - 2025-12-17
|
|
9
33
|
|
|
10
34
|
### Added
|
package/Functions/FetchAPI.js
CHANGED
|
@@ -7,10 +7,13 @@ export default async function fetchAPI(path, method = 'GET', body = null) {
|
|
|
7
7
|
body: JSON.stringify(body),
|
|
8
8
|
};
|
|
9
9
|
if (method === 'GET') delete req.body;
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
const data = await fetch(`https://teacupnet-backend.vercel.app/${path}`, req);
|
|
13
10
|
|
|
11
|
+
try {
|
|
12
|
+
const data = await fetch(
|
|
13
|
+
`https://teacupnet-backend.vercel.app/${path}`,
|
|
14
|
+
req
|
|
15
|
+
);
|
|
16
|
+
// const data = await fetch(`http://localhost:3000/${path}`, req);
|
|
14
17
|
return await data.json();
|
|
15
18
|
} catch (error) {
|
|
16
19
|
console.log(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "teacupnet-lib",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "A powerful JavaScript library for integrating TeacupNet analytics and data management. Track user interactions, page views, form completions, and seamlessly connect to the TeacupNet backend.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"analytics",
|
package/Analytics/fingerprint.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export default function fingerprint() {
|
|
2
|
-
const canvas = document.createElement('canvas');
|
|
3
|
-
canvas.width = 20;
|
|
4
|
-
canvas.height = 2220;
|
|
5
|
-
const ctx = canvas.getContext('2d');
|
|
6
|
-
ctx.fillText('Hello', 10, 10);
|
|
7
|
-
|
|
8
|
-
const data = canvas.toDataURL();
|
|
9
|
-
return data.slice(-32);
|
|
10
|
-
}
|