@neomanex/analytics-nuxt 1.0.2 → 1.0.3
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/module.mjs +2 -1
- package/dist/runtime/client.js +23 -3
- package/dist/runtime/config.js +2 -0
- package/dist/runtime/plugin.client.js +9 -5
- package/dist/runtime/server/api/_analytics.post.js +16 -13
- package/dist/runtime/server/middleware/page-visit.js +22 -5
- package/dist/runtime/server/plugins/correlation-meta.d.ts +0 -0
- package/dist/runtime/server/plugins/correlation-meta.js +9 -0
- package/package.json +1 -1
package/dist/module.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, addServerHandler, addPlugin, addImports, addTypeTemplate } from '@nuxt/kit';
|
|
1
|
+
import { defineNuxtModule, createResolver, addServerHandler, addServerPlugin, addPlugin, addImports, addTypeTemplate } from '@nuxt/kit';
|
|
2
2
|
import { defu } from 'defu';
|
|
3
3
|
|
|
4
4
|
const module = defineNuxtModule({
|
|
@@ -48,6 +48,7 @@ const module = defineNuxtModule({
|
|
|
48
48
|
route: "/api/_analytics",
|
|
49
49
|
handler: resolver.resolve("./runtime/server/api/_analytics.post")
|
|
50
50
|
});
|
|
51
|
+
addServerPlugin(resolver.resolve("./runtime/server/plugins/correlation-meta"));
|
|
51
52
|
addPlugin({
|
|
52
53
|
src: resolver.resolve("./runtime/plugin.server"),
|
|
53
54
|
mode: "server"
|
package/dist/runtime/client.js
CHANGED
|
@@ -25,10 +25,19 @@ export class AnalyticsClient {
|
|
|
25
25
|
event_type: eventType,
|
|
26
26
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27
27
|
source: this.config.source,
|
|
28
|
+
correlation_id: options?.correlation_id,
|
|
28
29
|
metadata: metadata ?? void 0,
|
|
29
30
|
account_id: options?.account_id,
|
|
30
|
-
user_id: options?.user_id
|
|
31
|
+
user_id: options?.user_id,
|
|
32
|
+
client_ip: options?.client_ip,
|
|
33
|
+
user_agent: options?.user_agent,
|
|
34
|
+
referer: options?.referer,
|
|
35
|
+
accept_language: options?.accept_language
|
|
31
36
|
};
|
|
37
|
+
if (event.metadata?.correlation_id) {
|
|
38
|
+
const { correlation_id: _, ...rest } = event.metadata;
|
|
39
|
+
event.metadata = Object.keys(rest).length > 0 ? rest : void 0;
|
|
40
|
+
}
|
|
32
41
|
if (this.queue.length >= this.config.maxQueueSize) {
|
|
33
42
|
this.handleQueueOverflow(event);
|
|
34
43
|
return;
|
|
@@ -72,17 +81,28 @@ export class AnalyticsClient {
|
|
|
72
81
|
get queueLength() {
|
|
73
82
|
return this.queue.length;
|
|
74
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Get the configured batch endpoint path (for sendBeacon usage).
|
|
86
|
+
*/
|
|
87
|
+
get batchEndpointUrl() {
|
|
88
|
+
return this.config.batchEndpoint;
|
|
89
|
+
}
|
|
75
90
|
/**
|
|
76
91
|
* Build an event payload for sendBeacon usage (browser page.leave).
|
|
77
92
|
* Returns the JSON string to send via navigator.sendBeacon.
|
|
78
93
|
*/
|
|
79
|
-
buildBeaconPayload(eventType, metadata) {
|
|
94
|
+
buildBeaconPayload(eventType, metadata, options) {
|
|
80
95
|
const event = {
|
|
81
96
|
event_type: eventType,
|
|
82
97
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
83
98
|
source: this.config.source,
|
|
99
|
+
correlation_id: options?.correlation_id,
|
|
84
100
|
metadata
|
|
85
101
|
};
|
|
102
|
+
if (event.metadata?.correlation_id) {
|
|
103
|
+
const { correlation_id: _, ...rest } = event.metadata;
|
|
104
|
+
event.metadata = Object.keys(rest).length > 0 ? rest : void 0;
|
|
105
|
+
}
|
|
86
106
|
return JSON.stringify({ events: [event] });
|
|
87
107
|
}
|
|
88
108
|
/**
|
|
@@ -114,7 +134,7 @@ export class AnalyticsClient {
|
|
|
114
134
|
* Send a batch of events via ofetch.
|
|
115
135
|
*/
|
|
116
136
|
async sendBatch(events) {
|
|
117
|
-
await $fetch(
|
|
137
|
+
await $fetch(this.config.batchEndpoint, {
|
|
118
138
|
baseURL: this.config.apiUrl,
|
|
119
139
|
method: "POST",
|
|
120
140
|
body: { events }
|
package/dist/runtime/config.js
CHANGED
|
@@ -4,11 +4,13 @@ export const DEFAULT_MAX_QUEUE_SIZE = 1e3;
|
|
|
4
4
|
export const DEFAULT_MAX_RETRIES = 3;
|
|
5
5
|
export const DEFAULT_RETRY_DELAY = 1e3;
|
|
6
6
|
export const DEFAULT_RETRY_BACKOFF_MULTIPLIER = 2;
|
|
7
|
+
export const DEFAULT_BATCH_ENDPOINT = "/api/v1/events/batch";
|
|
7
8
|
export const OVERFLOW_WARNING_DEBOUNCE_MS = 6e4;
|
|
8
9
|
export function resolveConfig(config) {
|
|
9
10
|
return {
|
|
10
11
|
apiUrl: config.apiUrl,
|
|
11
12
|
source: config.source,
|
|
13
|
+
batchEndpoint: config.batchEndpoint ?? DEFAULT_BATCH_ENDPOINT,
|
|
12
14
|
flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,
|
|
13
15
|
flushSize: config.flushSize ?? DEFAULT_FLUSH_SIZE,
|
|
14
16
|
maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,
|
|
@@ -37,7 +37,8 @@ export default defineNuxtPlugin(() => {
|
|
|
37
37
|
client = getInstance();
|
|
38
38
|
} catch {
|
|
39
39
|
client = init({
|
|
40
|
-
apiUrl: "
|
|
40
|
+
apiUrl: "",
|
|
41
|
+
batchEndpoint: "/api/_analytics",
|
|
41
42
|
source: publicConfig.source,
|
|
42
43
|
flushInterval: 1e4,
|
|
43
44
|
flushSize: 5
|
|
@@ -55,8 +56,9 @@ export default defineNuxtPlugin(() => {
|
|
|
55
56
|
screen_height: window.screen.height,
|
|
56
57
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
57
58
|
language: navigator.language,
|
|
58
|
-
correlation_id: correlationId,
|
|
59
59
|
session_id: sessionId
|
|
60
|
+
}, {
|
|
61
|
+
correlation_id: correlationId
|
|
60
62
|
});
|
|
61
63
|
isFirstPageView = false;
|
|
62
64
|
}
|
|
@@ -71,8 +73,9 @@ export default defineNuxtPlugin(() => {
|
|
|
71
73
|
query: to.query,
|
|
72
74
|
title: typeof document !== "undefined" ? document.title : void 0,
|
|
73
75
|
from_path: from.path,
|
|
74
|
-
correlation_id: getCorrelationId(),
|
|
75
76
|
session_id: sessionId
|
|
77
|
+
}, {
|
|
78
|
+
correlation_id: getCorrelationId()
|
|
76
79
|
});
|
|
77
80
|
});
|
|
78
81
|
}
|
|
@@ -84,10 +87,11 @@ export default defineNuxtPlugin(() => {
|
|
|
84
87
|
path: window.location.pathname,
|
|
85
88
|
duration_ms: durationMs,
|
|
86
89
|
scroll_depth_pct: getScrollDepthPct(),
|
|
87
|
-
correlation_id: getCorrelationId(),
|
|
88
90
|
session_id: sessionId
|
|
91
|
+
}, {
|
|
92
|
+
correlation_id: getCorrelationId()
|
|
89
93
|
});
|
|
90
|
-
navigator.sendBeacon(
|
|
94
|
+
navigator.sendBeacon(client.batchEndpointUrl, payload);
|
|
91
95
|
});
|
|
92
96
|
}
|
|
93
97
|
return {
|
|
@@ -33,19 +33,22 @@ export default defineEventHandler(async (event) => {
|
|
|
33
33
|
const acceptLanguage = getRequestHeader(event, "accept-language");
|
|
34
34
|
const origin = getRequestHeader(event, "origin");
|
|
35
35
|
const correlationId = getRequestHeader(event, "x-correlation-id");
|
|
36
|
-
const enrichedEvents = body.events.map((evt) =>
|
|
37
|
-
...evt
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
const enrichedEvents = body.events.map((evt) => {
|
|
37
|
+
const { correlation_id: metadataCorrelationId, ...restMetadata } = evt.metadata ?? {};
|
|
38
|
+
return {
|
|
39
|
+
...evt,
|
|
40
|
+
source: evt.source || analyticsConfig.source,
|
|
41
|
+
correlation_id: correlationId ?? evt.correlation_id ?? metadataCorrelationId,
|
|
42
|
+
client_ip: clientIp,
|
|
43
|
+
user_agent: userAgent,
|
|
44
|
+
referer,
|
|
45
|
+
accept_language: acceptLanguage,
|
|
46
|
+
metadata: {
|
|
47
|
+
...restMetadata,
|
|
48
|
+
origin
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
});
|
|
49
52
|
try {
|
|
50
53
|
const response = await $fetch("/api/v1/events/batch", {
|
|
51
54
|
baseURL: analyticsConfig.apiUrl,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineEventHandler, getRequestHeader, setResponseHeader, useRuntimeConfig, getRequestURL } from "#imports";
|
|
2
|
-
import { getInstance } from "../../client.js";
|
|
2
|
+
import { getInstance, init } from "../../client.js";
|
|
3
3
|
const EXCLUDED_PREFIXES = ["/api/", "/_nuxt/", "/__nuxt_error"];
|
|
4
4
|
const EXCLUDED_EXACT = ["/favicon.ico", "/_health", "/healthz"];
|
|
5
5
|
function extractClientIp(event) {
|
|
@@ -43,6 +43,7 @@ export default defineEventHandler((event) => {
|
|
|
43
43
|
}
|
|
44
44
|
const correlationId = generateCorrelationId();
|
|
45
45
|
setResponseHeader(event, "X-Correlation-Id", correlationId);
|
|
46
|
+
event.context.correlationId = correlationId;
|
|
46
47
|
const clientIp = extractClientIp(event);
|
|
47
48
|
const userAgent = getRequestHeader(event, "user-agent");
|
|
48
49
|
const referer = getRequestHeader(event, "referer");
|
|
@@ -51,16 +52,32 @@ export default defineEventHandler((event) => {
|
|
|
51
52
|
url.searchParams.forEach((value, key) => {
|
|
52
53
|
query[key] = value;
|
|
53
54
|
});
|
|
55
|
+
let client;
|
|
56
|
+
try {
|
|
57
|
+
client = getInstance();
|
|
58
|
+
} catch {
|
|
59
|
+
try {
|
|
60
|
+
const analyticsApiConfig = config.analytics;
|
|
61
|
+
client = init({
|
|
62
|
+
apiUrl: analyticsApiConfig.apiUrl,
|
|
63
|
+
source: analyticsApiConfig.source,
|
|
64
|
+
flushInterval: 5e3,
|
|
65
|
+
flushSize: 20
|
|
66
|
+
});
|
|
67
|
+
} catch {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
54
71
|
try {
|
|
55
|
-
const client = getInstance();
|
|
56
72
|
client.track("page.visit", {
|
|
57
73
|
path,
|
|
58
|
-
query: Object.keys(query).length > 0 ? query : void 0
|
|
59
|
-
|
|
74
|
+
query: Object.keys(query).length > 0 ? query : void 0
|
|
75
|
+
}, {
|
|
60
76
|
client_ip: clientIp,
|
|
61
77
|
user_agent: userAgent,
|
|
62
78
|
referer,
|
|
63
|
-
accept_language: acceptLanguage
|
|
79
|
+
accept_language: acceptLanguage,
|
|
80
|
+
correlation_id: correlationId
|
|
64
81
|
});
|
|
65
82
|
} catch {
|
|
66
83
|
}
|
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { defineNitroPlugin } from "#imports";
|
|
2
|
+
export default defineNitroPlugin((nitroApp) => {
|
|
3
|
+
nitroApp.hooks.hook("render:html", (html, { event }) => {
|
|
4
|
+
const correlationId = event.context.correlationId;
|
|
5
|
+
if (correlationId) {
|
|
6
|
+
html.head.push(`<meta name="x-correlation-id" content="${correlationId}">`);
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
});
|
package/package.json
CHANGED