@mcp-fe/react-event-tracker 0.0.2

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/.babelrc ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "presets": [
3
+ [
4
+ "@nx/react/babel",
5
+ {
6
+ "runtime": "automatic",
7
+ "useBuiltIns": "usage"
8
+ }
9
+ ]
10
+ ],
11
+ "plugins": []
12
+ }
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # react-event-tracker
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Running unit tests
6
+
7
+ Run `nx test react-event-tracker` to execute the unit tests via [Vitest](https://vitest.dev/).
@@ -0,0 +1,12 @@
1
+ import nx from '@nx/eslint-plugin';
2
+ import baseConfig from '../../eslint.config.mjs';
3
+
4
+ export default [
5
+ ...baseConfig,
6
+ ...nx.configs['flat/react'],
7
+ {
8
+ files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
9
+ // Override or add rules here
10
+ rules: {},
11
+ },
12
+ ];
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@mcp-fe/react-event-tracker",
3
+ "version": "0.0.2",
4
+ "main": "./index.js",
5
+ "types": "./index.d.ts",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./index.mjs",
9
+ "require": "./index.js"
10
+ }
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ }
15
+ }
package/project.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "react-event-tracker",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "libs/react-event-tracker/src",
5
+ "projectType": "library",
6
+ "tags": [],
7
+ "// targets": "to see all targets run: nx show project react-event-tracker --web",
8
+ "targets": {}
9
+ }
@@ -0,0 +1,97 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { useLocation } from 'react-router-dom';
3
+ import {
4
+ initEventTracker,
5
+ trackNavigation,
6
+ trackClick,
7
+ trackInput,
8
+ } from '@mcp-fe/event-tracker';
9
+
10
+ export function useReactRouterEventTracker(): void {
11
+ const location = useLocation();
12
+ const [isInitialized, setIsInitialized] = useState(false);
13
+
14
+ const currentPathRef = useRef(location.pathname);
15
+ const prevPathRef = useRef<string | null>(null);
16
+
17
+ useEffect(() => {
18
+ prevPathRef.current = currentPathRef.current;
19
+ currentPathRef.current = location.pathname;
20
+ }, [location.pathname]);
21
+
22
+ useEffect(() => {
23
+ initEventTracker()
24
+ .then(() => {
25
+ setIsInitialized(true);
26
+ })
27
+ .catch((error) => {
28
+ console.error('Worker initialization failed:', error);
29
+ });
30
+ }, []);
31
+
32
+ useEffect(() => {
33
+ if (!isInitialized) return;
34
+
35
+ const prevPath = prevPathRef.current;
36
+ const currentPath = currentPathRef.current;
37
+
38
+ if (prevPath && prevPath !== currentPath) {
39
+ trackNavigation(prevPath, currentPath, currentPath).catch((error) => {
40
+ console.error('Failed to track navigation:', error);
41
+ });
42
+ }
43
+ }, [location.pathname, isInitialized]);
44
+
45
+ useEffect(() => {
46
+ if (!isInitialized) return;
47
+
48
+ const handleClick = (event: MouseEvent): void => {
49
+ const target = event.target as HTMLElement;
50
+ if (!target) return;
51
+
52
+ const isInteractive =
53
+ target.tagName === 'BUTTON' ||
54
+ target.tagName === 'A' ||
55
+ target.closest('button') ||
56
+ target.closest('a');
57
+
58
+ if (!isInteractive) {
59
+ trackClick(target, currentPathRef.current).catch((error) => {
60
+ console.error('Failed to track click:', error);
61
+ });
62
+ }
63
+ };
64
+
65
+ document.addEventListener('click', handleClick, true);
66
+ return () => document.removeEventListener('click', handleClick, true);
67
+ }, [isInitialized]);
68
+
69
+ useEffect(() => {
70
+ if (!isInitialized) return;
71
+
72
+ let timeoutId: ReturnType<typeof setTimeout>;
73
+
74
+ const handleInput = (event: Event): void => {
75
+ const target = event.target as HTMLInputElement | HTMLTextAreaElement;
76
+ if (
77
+ target &&
78
+ (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA')
79
+ ) {
80
+ clearTimeout(timeoutId);
81
+ timeoutId = setTimeout(() => {
82
+ trackInput(target, target.value, currentPathRef.current).catch(
83
+ (error) => {
84
+ console.error('Failed to track input:', error);
85
+ },
86
+ );
87
+ }, 1000); // Debounce by 1 second
88
+ }
89
+ };
90
+
91
+ document.addEventListener('input', handleInput, true);
92
+ return () => {
93
+ clearTimeout(timeoutId);
94
+ document.removeEventListener('input', handleInput, true);
95
+ };
96
+ }, [isInitialized]);
97
+ }
@@ -0,0 +1,97 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { useLocation } from '@tanstack/react-router';
3
+ import {
4
+ initEventTracker,
5
+ trackNavigation,
6
+ trackClick,
7
+ trackInput,
8
+ } from '@mcp-fe/event-tracker';
9
+
10
+ export function useTanstackRouterEventTracker(): void {
11
+ const location = useLocation();
12
+ const [isInitialized, setIsInitialized] = useState(false);
13
+
14
+ const currentPathRef = useRef(location.pathname);
15
+ const prevPathRef = useRef<string | null>(null);
16
+
17
+ useEffect(() => {
18
+ if (currentPathRef.current !== location.pathname) {
19
+ prevPathRef.current = currentPathRef.current;
20
+ currentPathRef.current = location.pathname;
21
+ }
22
+ }, [location.pathname]);
23
+
24
+ useEffect(() => {
25
+ initEventTracker()
26
+ .then(() => setIsInitialized(true))
27
+ .catch((error) => {
28
+ console.error('Worker initialization failed:', error);
29
+ });
30
+ }, []);
31
+
32
+ useEffect(() => {
33
+ if (!isInitialized) return;
34
+
35
+ const prevPath = prevPathRef.current;
36
+ const currentPath = currentPathRef.current;
37
+
38
+ if (prevPath && prevPath !== currentPath) {
39
+ trackNavigation(prevPath, currentPath, currentPath).catch((error) => {
40
+ console.error('Failed to track navigation:', error);
41
+ });
42
+ }
43
+ }, [location.pathname, isInitialized]);
44
+
45
+ useEffect(() => {
46
+ if (!isInitialized) return;
47
+
48
+ const handleClick = (event: MouseEvent): void => {
49
+ const target = event.target as HTMLElement;
50
+ if (!target) return;
51
+
52
+ const isInteractive =
53
+ target.tagName === 'BUTTON' ||
54
+ target.tagName === 'A' ||
55
+ target.closest('button') ||
56
+ target.closest('a');
57
+
58
+ if (!isInteractive) {
59
+ trackClick(target, currentPathRef.current).catch((error) => {
60
+ console.error('Failed to track click:', error);
61
+ });
62
+ }
63
+ };
64
+
65
+ document.addEventListener('click', handleClick, true);
66
+ return () => document.removeEventListener('click', handleClick, true);
67
+ }, [isInitialized]);
68
+
69
+ useEffect(() => {
70
+ if (!isInitialized) return;
71
+
72
+ let timeoutId: ReturnType<typeof setTimeout>;
73
+
74
+ const handleInput = (event: Event): void => {
75
+ const target = event.target as HTMLInputElement | HTMLTextAreaElement;
76
+ if (
77
+ target &&
78
+ (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA')
79
+ ) {
80
+ clearTimeout(timeoutId);
81
+ timeoutId = setTimeout(() => {
82
+ trackInput(target, target.value, currentPathRef.current).catch(
83
+ (error) => {
84
+ console.error('Failed to track input:', error);
85
+ },
86
+ );
87
+ }, 1000);
88
+ }
89
+ };
90
+
91
+ document.addEventListener('input', handleInput, true);
92
+ return () => {
93
+ clearTimeout(timeoutId);
94
+ document.removeEventListener('input', handleInput, true);
95
+ };
96
+ }, [isInitialized]);
97
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './hooks/useTanstackRouterEventTracker';
2
+ export * from './hooks/useReactRouterEventTracker';
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "jsx": "react-jsx",
4
+ "allowJs": false,
5
+ "esModuleInterop": false,
6
+ "allowSyntheticDefaultImports": true,
7
+ "strict": true,
8
+ "types": ["vite/client", "vitest"],
9
+ "lib": ["dom", "esnext"]
10
+ },
11
+ "files": [],
12
+ "include": [],
13
+ "references": [
14
+ {
15
+ "path": "./tsconfig.lib.json"
16
+ },
17
+ {
18
+ "path": "./tsconfig.spec.json"
19
+ }
20
+ ],
21
+ "extends": "../../tsconfig.base.json"
22
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "types": [
6
+ "node",
7
+ "@nx/react/typings/cssmodule.d.ts",
8
+ "@nx/react/typings/image.d.ts",
9
+ "vite/client"
10
+ ]
11
+ },
12
+ "exclude": [
13
+ "**/*.spec.ts",
14
+ "**/*.test.ts",
15
+ "**/*.spec.tsx",
16
+ "**/*.test.tsx",
17
+ "**/*.spec.js",
18
+ "**/*.test.js",
19
+ "**/*.spec.jsx",
20
+ "**/*.test.jsx",
21
+ "vite.config.ts",
22
+ "vite.config.mts",
23
+ "vitest.config.ts",
24
+ "vitest.config.mts",
25
+ "src/**/*.test.ts",
26
+ "src/**/*.spec.ts",
27
+ "src/**/*.test.tsx",
28
+ "src/**/*.spec.tsx",
29
+ "src/**/*.test.js",
30
+ "src/**/*.spec.js",
31
+ "src/**/*.test.jsx",
32
+ "src/**/*.spec.jsx"
33
+ ],
34
+ "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"],
35
+ "references": []
36
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "types": [
6
+ "vitest/globals",
7
+ "vitest/importMeta",
8
+ "vite/client",
9
+ "node",
10
+ "vitest"
11
+ ]
12
+ },
13
+ "include": [
14
+ "vite.config.ts",
15
+ "vite.config.mts",
16
+ "vitest.config.ts",
17
+ "vitest.config.mts",
18
+ "src/**/*.test.ts",
19
+ "src/**/*.spec.ts",
20
+ "src/**/*.test.tsx",
21
+ "src/**/*.spec.tsx",
22
+ "src/**/*.test.js",
23
+ "src/**/*.spec.js",
24
+ "src/**/*.test.jsx",
25
+ "src/**/*.spec.jsx",
26
+ "src/**/*.d.ts"
27
+ ]
28
+ }
@@ -0,0 +1,61 @@
1
+ /// <reference types='vitest' />
2
+ import { defineConfig } from 'vite';
3
+ import react from '@vitejs/plugin-react';
4
+ import dts from 'vite-plugin-dts';
5
+ import * as path from 'path';
6
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
7
+ import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
8
+
9
+ export default defineConfig(() => ({
10
+ root: import.meta.dirname,
11
+ cacheDir: '../../node_modules/.vite/libs/react-event-tracker',
12
+ plugins: [
13
+ react(),
14
+ nxViteTsPaths(),
15
+ nxCopyAssetsPlugin(['*.md']),
16
+ dts({
17
+ entryRoot: 'src',
18
+ tsconfigPath: path.join(import.meta.dirname, 'tsconfig.lib.json'),
19
+ pathsToAliases: false,
20
+ }),
21
+ ],
22
+ // Uncomment this if you are using workers.
23
+ // worker: {
24
+ // plugins: () => [ nxViteTsPaths() ],
25
+ // },
26
+ // Configuration for building your library.
27
+ // See: https://vite.dev/guide/build.html#library-mode
28
+ build: {
29
+ outDir: '../../dist/libs/react-event-tracker',
30
+ emptyOutDir: true,
31
+ reportCompressedSize: true,
32
+ commonjsOptions: {
33
+ transformMixedEsModules: true,
34
+ },
35
+ lib: {
36
+ // Could also be a dictionary or array of multiple entry points.
37
+ entry: 'src/index.ts',
38
+ name: 'react-event-tracker',
39
+ fileName: 'index',
40
+ // Change this to the formats you want to support.
41
+ // Don't forget to update your package.json as well.
42
+ formats: ['es' as const],
43
+ },
44
+ rollupOptions: {
45
+ // External packages that should not be bundled into your library.
46
+ external: ['react', 'react-dom', 'react/jsx-runtime'],
47
+ },
48
+ },
49
+ test: {
50
+ name: 'react-event-tracker',
51
+ watch: false,
52
+ globals: true,
53
+ environment: 'jsdom',
54
+ include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
55
+ reporters: ['default'],
56
+ coverage: {
57
+ reportsDirectory: '../../coverage/libs/react-event-tracker',
58
+ provider: 'v8' as const,
59
+ },
60
+ },
61
+ }));