nuxt-visitors 1.1.2 โ 1.2.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 +51 -6
- package/dist/module.d.mts +10 -2
- package/dist/module.json +3 -3
- package/dist/module.mjs +17 -6
- package/dist/runtime/app/composables/useVisitors.d.ts +19 -0
- package/dist/runtime/app/composables/useVisitors.js +10 -2
- package/dist/runtime/app/plugins/location.server.d.ts +2 -0
- package/dist/runtime/app/plugins/location.server.js +9 -0
- package/dist/runtime/server/routes/locations.d.ts +2 -0
- package/dist/runtime/server/routes/locations.js +17 -0
- package/dist/types.d.mts +1 -7
- package/package.json +18 -20
- package/dist/module.cjs +0 -5
- package/dist/module.d.ts +0 -5
- package/dist/types.d.ts +0 -7
package/README.md
CHANGED
@@ -19,6 +19,14 @@ Add live visitor counting to your Nuxt website in seconds. WebSocket-based, type
|
|
19
19
|
- ๐งน Auto cleanup on unmount
|
20
20
|
- ๐ Leverages [Nitro WebSocket](https://nitro.unjs.io/guide/websocket) with Pub/Sub
|
21
21
|
|
22
|
+
> **New (Experimental):** Anonymous tracking of visitors' locations!
|
23
|
+
>
|
24
|
+
> When enabled, the module tracks visitor locations. The `useVisitors` composable will additionally provide:
|
25
|
+
> - A `locations` array: lists locations of all visitors.
|
26
|
+
> - A `myLocation` object: contains the specific geolocation data for the current visitor.
|
27
|
+
>
|
28
|
+
> **Note:** This feature is experimental and may be subject to future changes.
|
29
|
+
|
22
30
|
## Installation
|
23
31
|
|
24
32
|
Install the module to your Nuxt application with one command:
|
@@ -40,6 +48,27 @@ export default defineNuxtConfig({
|
|
40
48
|
})
|
41
49
|
```
|
42
50
|
|
51
|
+
### Configuration
|
52
|
+
|
53
|
+
The module accepts an optional configuration to enable location tracking. In your `nuxt.config.ts`, you can enable it as follows:
|
54
|
+
|
55
|
+
```ts
|
56
|
+
export default defineNuxtConfig({
|
57
|
+
modules: ['nuxt-visitors'],
|
58
|
+
visitors: {
|
59
|
+
// Set to true to enable tracking of visitor locations
|
60
|
+
locations: true
|
61
|
+
},
|
62
|
+
nitro: {
|
63
|
+
experimental: {
|
64
|
+
websocket: true
|
65
|
+
}
|
66
|
+
}
|
67
|
+
})
|
68
|
+
```
|
69
|
+
|
70
|
+
This will enable the `locations` and `myLocation` properties in the composable, as well as the `locations` property in the composable's return object.
|
71
|
+
|
43
72
|
## Usage
|
44
73
|
|
45
74
|
```vue
|
@@ -63,11 +92,13 @@ That's it! The module handles everything else automatically:
|
|
63
92
|
The composable provides additional features:
|
64
93
|
```ts
|
65
94
|
const {
|
66
|
-
visitors, // Ref<number> - Current visitor count
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
95
|
+
visitors, // Ref<number> - Current visitor count
|
96
|
+
locations, // Ref<Location[]> - Array of geolocation objects for all visitors (if `locations: true`)
|
97
|
+
myLocation, // Ref<Location | null> - Geolocation of the current visitor (if `locations: true`)
|
98
|
+
isConnected, // Ref<boolean> - Connection status
|
99
|
+
isLoading, // Ref<boolean> - Loading state
|
100
|
+
error, // Ref<string | null> - Error message if any
|
101
|
+
reconnect // () => void - Manual reconnection
|
71
102
|
} = useVisitors()
|
72
103
|
```
|
73
104
|
|
@@ -91,6 +122,20 @@ To start contributing, you can follow these steps:
|
|
91
122
|
|
92
123
|
<!-- /automd -->
|
93
124
|
|
125
|
+
<!-- automd:fetch url="gh:hugorcd/markdown/main/src/sponsors.md" -->
|
126
|
+
|
127
|
+
## Sponsors
|
128
|
+
|
129
|
+
<p align="center">
|
130
|
+
<a href="https://cdn.jsdelivr.net/gh/hugorcd/static/sponsors.svg">
|
131
|
+
<img src='https://cdn.jsdelivr.net/gh/hugorcd/static/sponsors.svg' alt="HugoRCD sponsors" />
|
132
|
+
</a>
|
133
|
+
</p>
|
134
|
+
|
135
|
+
<!-- /automd -->
|
136
|
+
|
137
|
+
## Contributors
|
138
|
+
|
94
139
|
<!-- automd:contributors license=Apache author=HugoRCD github=HugoRCD/nuxt-visitors -->
|
95
140
|
|
96
141
|
Published under the [APACHE](https://github.com/HugoRCD/nuxt-visitors/blob/main/LICENSE) license.
|
@@ -106,6 +151,6 @@ Made by [@HugoRCD](https://github.com/HugoRCD) and [community](https://github.co
|
|
106
151
|
|
107
152
|
---
|
108
153
|
|
109
|
-
_๐ค auto updated with [automd](https://automd.unjs.io) (last updated:
|
154
|
+
_๐ค auto updated with [automd](https://automd.unjs.io) (last updated: Mon Mar 31 2025)_
|
110
155
|
|
111
156
|
<!-- /automd -->
|
package/dist/module.d.mts
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
2
2
|
|
3
|
-
|
3
|
+
type ModuleOptions = {
|
4
|
+
/**
|
5
|
+
* Allows for the anonymous tracking of current visitors' locations.
|
6
|
+
* The composable `useVisitors` will provide the visitor count along with an additional `locations` array and a `myLocation` object that contains the visitors' locations.
|
7
|
+
* @default false
|
8
|
+
*/
|
9
|
+
locations?: boolean;
|
10
|
+
};
|
11
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
4
12
|
|
5
|
-
export { _default as default };
|
13
|
+
export { type ModuleOptions, _default as default };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
@@ -1,17 +1,28 @@
|
|
1
|
-
import { defineNuxtModule, createResolver, addImportsDir, addServerHandler } from '@nuxt/kit';
|
1
|
+
import { defineNuxtModule, createResolver, addImportsDir, addPlugin, addServerHandler } from '@nuxt/kit';
|
2
2
|
|
3
3
|
const module = defineNuxtModule({
|
4
4
|
meta: {
|
5
5
|
name: "nuxt-visitors",
|
6
6
|
configKey: "visitors"
|
7
7
|
},
|
8
|
-
|
8
|
+
defaults: {
|
9
|
+
locations: false
|
10
|
+
},
|
11
|
+
setup(options, nuxt) {
|
9
12
|
const resolver = createResolver(import.meta.url);
|
10
13
|
addImportsDir(resolver.resolve("./runtime/app/composables"));
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
if (options.locations) {
|
15
|
+
addPlugin(resolver.resolve("./runtime/app/plugins/location.server"));
|
16
|
+
addServerHandler({
|
17
|
+
route: "/.nuxt-visitors/ws",
|
18
|
+
handler: resolver.resolve("./runtime/server/routes/locations")
|
19
|
+
});
|
20
|
+
} else {
|
21
|
+
addServerHandler({
|
22
|
+
route: "/.nuxt-visitors/ws",
|
23
|
+
handler: resolver.resolve("./runtime/server/routes/visitors")
|
24
|
+
});
|
25
|
+
}
|
15
26
|
}
|
16
27
|
});
|
17
28
|
|
@@ -10,6 +10,8 @@
|
|
10
10
|
*
|
11
11
|
* @returns {Object} An object containing:
|
12
12
|
* - visitors: Ref<number> - Current number of visitors
|
13
|
+
* - locations: Ref<Location[]> - Array of geolocation objects for all visitors (if `locations: true`)
|
14
|
+
* - myLocation: Ref<Location | null> - Geolocation of the current visitor (if `locations: true`)
|
13
15
|
* - isLoading: Ref<boolean> - Loading state indicator
|
14
16
|
* - error: Ref<string | null> - Error message if any
|
15
17
|
* - isConnected: Ref<boolean> - WebSocket connection status
|
@@ -17,6 +19,23 @@
|
|
17
19
|
*/
|
18
20
|
export declare function useVisitors(): {
|
19
21
|
visitors: import("vue").Ref<number, number>;
|
22
|
+
locations: import("vue").Ref<{
|
23
|
+
latitude: number;
|
24
|
+
longitude: number;
|
25
|
+
}[], {
|
26
|
+
latitude: number;
|
27
|
+
longitude: number;
|
28
|
+
}[] | {
|
29
|
+
latitude: number;
|
30
|
+
longitude: number;
|
31
|
+
}[]>;
|
32
|
+
myLocation: import("vue").Ref<{
|
33
|
+
latitude: number;
|
34
|
+
longitude: number;
|
35
|
+
}, {
|
36
|
+
latitude: number;
|
37
|
+
longitude: number;
|
38
|
+
}>;
|
20
39
|
isLoading: import("vue").Ref<boolean, boolean>;
|
21
40
|
error: import("vue").Ref<string | null, string | null>;
|
22
41
|
isConnected: import("vue").Ref<boolean, boolean>;
|
@@ -2,6 +2,11 @@ import { ref, onMounted, onBeforeUnmount } from "vue";
|
|
2
2
|
import { useState } from "#imports";
|
3
3
|
export function useVisitors() {
|
4
4
|
const visitors = useState("visitors", () => 0);
|
5
|
+
const locations = ref([]);
|
6
|
+
const myLocation = useState("location", () => ({
|
7
|
+
latitude: 0,
|
8
|
+
longitude: 0
|
9
|
+
}));
|
5
10
|
const isLoading = ref(true);
|
6
11
|
const error = ref(null);
|
7
12
|
const wsRef = ref(null);
|
@@ -12,7 +17,7 @@ export function useVisitors() {
|
|
12
17
|
const getWebSocketUrl = () => {
|
13
18
|
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
14
19
|
const baseUrl = window.location.host.replace(/^(http|https):\/\//, "");
|
15
|
-
return `${protocol}//${baseUrl}/.nuxt-visitors/ws`;
|
20
|
+
return `${protocol}//${baseUrl}/.nuxt-visitors/ws?latitude=${myLocation.value.latitude}&longitude=${myLocation.value.longitude}`;
|
16
21
|
};
|
17
22
|
const cleanup = () => {
|
18
23
|
if (wsRef.value) {
|
@@ -26,7 +31,8 @@ export function useVisitors() {
|
|
26
31
|
if (!isMounted.value) return;
|
27
32
|
try {
|
28
33
|
const data = typeof event.data === "string" ? event.data : await event.data.text();
|
29
|
-
|
34
|
+
locations.value = JSON.parse(data);
|
35
|
+
const visitorCount = locations.value.length;
|
30
36
|
if (!isNaN(visitorCount) && visitorCount >= 0) {
|
31
37
|
visitors.value = visitorCount;
|
32
38
|
} else {
|
@@ -94,6 +100,8 @@ export function useVisitors() {
|
|
94
100
|
});
|
95
101
|
return {
|
96
102
|
visitors,
|
103
|
+
locations,
|
104
|
+
myLocation,
|
97
105
|
isLoading,
|
98
106
|
error,
|
99
107
|
isConnected,
|
@@ -0,0 +1,9 @@
|
|
1
|
+
export default defineNuxtPlugin(() => {
|
2
|
+
const event = useRequestEvent();
|
3
|
+
useState("location", () => ({
|
4
|
+
latitude: event?.context.cf?.latitude || Math.random() * 180 - 90,
|
5
|
+
// default to random latitude (only in dev)
|
6
|
+
longitude: event?.context.cf?.longitude || Math.random() * 360 - 180
|
7
|
+
// default to random longitude (only in dev)
|
8
|
+
}));
|
9
|
+
});
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { defineWebSocketHandler } from "h3";
|
2
|
+
import { getQuery } from "ufo";
|
3
|
+
export default defineWebSocketHandler({
|
4
|
+
open(peer) {
|
5
|
+
const locations = Array.from(peer.peers.values()).map((peer2) => getQuery(peer2.websocket.url));
|
6
|
+
peer.subscribe("nuxt-visitors");
|
7
|
+
peer.publish("nuxt-visitors", JSON.stringify(locations));
|
8
|
+
peer.send(JSON.stringify(locations));
|
9
|
+
},
|
10
|
+
close(peer) {
|
11
|
+
peer.unsubscribe("nuxt-visitors");
|
12
|
+
setTimeout(() => {
|
13
|
+
const locations = Array.from(peer.peers.values()).map((peer2) => getQuery(peer2.websocket.url));
|
14
|
+
peer.publish("nuxt-visitors", JSON.stringify(locations));
|
15
|
+
}, 500);
|
16
|
+
}
|
17
|
+
});
|
package/dist/types.d.mts
CHANGED
@@ -1,7 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
import type { default as Module } from './module.js'
|
4
|
-
|
5
|
-
export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
|
6
|
-
|
7
|
-
export { default } from './module.js'
|
1
|
+
export { type ModuleOptions, default } from './module.mjs'
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "nuxt-visitors",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.2.0",
|
4
4
|
"description": "Add real-time visitor tracking to your Nuxt app with one line of code. WebSocket made easy",
|
5
5
|
"license": "Apache-2.0",
|
6
6
|
"type": "module",
|
@@ -15,13 +15,11 @@
|
|
15
15
|
"author": "HugoRCD",
|
16
16
|
"exports": {
|
17
17
|
".": {
|
18
|
-
"types": "./dist/
|
19
|
-
"import": "./dist/module.mjs"
|
20
|
-
"require": "./dist/module.cjs"
|
18
|
+
"types": "./dist/module.d.mts",
|
19
|
+
"import": "./dist/module.mjs"
|
21
20
|
}
|
22
21
|
},
|
23
|
-
"main": "./dist/module.
|
24
|
-
"types": "./dist/types.d.ts",
|
22
|
+
"main": "./dist/module.mjs",
|
25
23
|
"files": [
|
26
24
|
"dist"
|
27
25
|
],
|
@@ -39,22 +37,22 @@
|
|
39
37
|
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
40
38
|
},
|
41
39
|
"dependencies": {
|
42
|
-
"@nuxt/kit": "3.
|
40
|
+
"@nuxt/kit": "3.16.2",
|
41
|
+
"ufo": "^1.6.1"
|
43
42
|
},
|
44
43
|
"devDependencies": {
|
45
|
-
"@hrcd/eslint-config": "
|
46
|
-
"@nuxt/devtools": "^
|
47
|
-
"@nuxt/
|
48
|
-
"@nuxt/
|
49
|
-
"@nuxt/
|
50
|
-
"@nuxt/test-utils": "^3.15.4",
|
44
|
+
"@hrcd/eslint-config": "edge",
|
45
|
+
"@nuxt/devtools": "^2.4.0",
|
46
|
+
"@nuxt/module-builder": "^1.0.1",
|
47
|
+
"@nuxt/schema": "3.16.2",
|
48
|
+
"@nuxt/test-utils": "^3.17.2",
|
51
49
|
"@types/node": "latest",
|
52
|
-
"automd": "^0.
|
53
|
-
"eslint": "^9.
|
54
|
-
"nuxt": "3.
|
55
|
-
"typescript": "5.
|
56
|
-
"vitest": "^3.
|
57
|
-
"vue-tsc": "^2.2.
|
50
|
+
"automd": "^0.4.0",
|
51
|
+
"eslint": "^9.24.0",
|
52
|
+
"nuxt": "3.16.0",
|
53
|
+
"typescript": "5.8.3",
|
54
|
+
"vitest": "^3.1.1",
|
55
|
+
"vue-tsc": "^2.2.8"
|
58
56
|
},
|
59
|
-
"packageManager": "pnpm@
|
57
|
+
"packageManager": "pnpm@10.8.1"
|
60
58
|
}
|
package/dist/module.cjs
DELETED
package/dist/module.d.ts
DELETED
package/dist/types.d.ts
DELETED