@jmp-technologies/analytics 0.1.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 +7 -0
- package/dist/index.cjs +191 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +42 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +1 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# `@jmp-technologies/analytics`
|
|
2
|
+
|
|
3
|
+
Browser client for **`POST {baseUrl}/api/events`**. Full API, CORS, and examples: **[docs/tracking-integration.md](../../docs/tracking-integration.md)**.
|
|
4
|
+
|
|
5
|
+
**Build:** `npm run build` in this package (or root `prepare` after `npm install`).
|
|
6
|
+
|
|
7
|
+
**Publish to npm (org `jmp-technologies`):** `npm publish` from this directory after `npm run build` (package sets `publishConfig.access` to `public`).
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createJmpAnalytics: () => createJmpAnalytics
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
var DEFAULT_SOURCE = "direct";
|
|
27
|
+
var SESSION_STORAGE_KEY = "jmp_analytics_sid";
|
|
28
|
+
function normalizeBaseUrl(baseUrl) {
|
|
29
|
+
return baseUrl.replace(/\/$/, "");
|
|
30
|
+
}
|
|
31
|
+
function inferDevice() {
|
|
32
|
+
if (typeof window === "undefined") {
|
|
33
|
+
return "unknown";
|
|
34
|
+
}
|
|
35
|
+
return window.innerWidth < 768 ? "mobile" : "desktop";
|
|
36
|
+
}
|
|
37
|
+
function getOrCreateSessionId() {
|
|
38
|
+
if (typeof window === "undefined" || !globalThis.crypto?.randomUUID) {
|
|
39
|
+
return void 0;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
let id = sessionStorage.getItem(SESSION_STORAGE_KEY);
|
|
43
|
+
if (!id) {
|
|
44
|
+
id = crypto.randomUUID();
|
|
45
|
+
sessionStorage.setItem(SESSION_STORAGE_KEY, id);
|
|
46
|
+
}
|
|
47
|
+
return id;
|
|
48
|
+
} catch {
|
|
49
|
+
return void 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function createJmpAnalytics(config) {
|
|
53
|
+
const baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
54
|
+
const resolvedFetch = config.fetchImpl ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
55
|
+
if (!resolvedFetch) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
"createJmpAnalytics: fetch is not available; pass fetchImpl in config"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const fetchFn = resolvedFetch;
|
|
61
|
+
let lastDedupePath = "";
|
|
62
|
+
let lastDedupeAt = 0;
|
|
63
|
+
async function send(body) {
|
|
64
|
+
const url = `${baseUrl}/api/events`;
|
|
65
|
+
const headers = {
|
|
66
|
+
"Content-Type": "application/json",
|
|
67
|
+
"X-JMP-Tracking-Key": config.trackingKey
|
|
68
|
+
};
|
|
69
|
+
const sid = getOrCreateSessionId();
|
|
70
|
+
const payload = { ...body, ...sid ? { sessionId: sid } : {} };
|
|
71
|
+
if (config.debug) {
|
|
72
|
+
console.debug("[@jmp-technologies/analytics]", "POST", url, payload);
|
|
73
|
+
}
|
|
74
|
+
const res = await fetchFn(url, {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers,
|
|
77
|
+
body: JSON.stringify(payload),
|
|
78
|
+
keepalive: true
|
|
79
|
+
});
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
let detail = "";
|
|
82
|
+
try {
|
|
83
|
+
const err = await res.json();
|
|
84
|
+
detail = err.error ?? await res.text();
|
|
85
|
+
} catch {
|
|
86
|
+
detail = await res.text();
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`JMP Analytics HTTP ${res.status}: ${detail}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function shouldDedupePageView(path) {
|
|
92
|
+
const ms = config.pageViewDedupeMs ?? 0;
|
|
93
|
+
if (ms <= 0 || typeof window === "undefined") {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
if (path === lastDedupePath && now - lastDedupeAt < ms) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
lastDedupePath = path;
|
|
101
|
+
lastDedupeAt = now;
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
function trackPageView(options) {
|
|
105
|
+
const path = options?.path ?? (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
106
|
+
if (shouldDedupePageView(path)) {
|
|
107
|
+
return Promise.resolve();
|
|
108
|
+
}
|
|
109
|
+
return send({
|
|
110
|
+
type: "page_view",
|
|
111
|
+
path: path || "/",
|
|
112
|
+
source: options?.source ?? DEFAULT_SOURCE,
|
|
113
|
+
device: options?.device ?? inferDevice(),
|
|
114
|
+
country: options?.country,
|
|
115
|
+
metadata: options?.metadata && typeof options.metadata === "object" ? options.metadata : {}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function trackFormSubmit(path, metadata) {
|
|
119
|
+
return send({
|
|
120
|
+
type: "form_submit",
|
|
121
|
+
path: path || "/",
|
|
122
|
+
metadata: metadata ?? {}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function trackCallClick(path, metadata) {
|
|
126
|
+
return send({
|
|
127
|
+
type: "call_click",
|
|
128
|
+
path: path || "/",
|
|
129
|
+
metadata: metadata ?? {}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function track(type, payload) {
|
|
133
|
+
if (type === "page_view") {
|
|
134
|
+
return trackPageView({
|
|
135
|
+
path: payload.path,
|
|
136
|
+
source: payload.source,
|
|
137
|
+
device: payload.device,
|
|
138
|
+
country: payload.country,
|
|
139
|
+
metadata: payload.metadata
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (type === "form_submit") {
|
|
143
|
+
return trackFormSubmit(payload.path, payload.metadata);
|
|
144
|
+
}
|
|
145
|
+
return trackCallClick(payload.path, payload.metadata);
|
|
146
|
+
}
|
|
147
|
+
function subscribeToSpaNavigation() {
|
|
148
|
+
if (typeof window === "undefined") {
|
|
149
|
+
return () => {
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
let lastPath = window.location.pathname;
|
|
153
|
+
const run = () => {
|
|
154
|
+
const path = window.location.pathname;
|
|
155
|
+
if (path !== lastPath) {
|
|
156
|
+
lastPath = path;
|
|
157
|
+
void trackPageView({ path });
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
window.addEventListener("popstate", run);
|
|
161
|
+
const push = history.pushState.bind(history);
|
|
162
|
+
const replace = history.replaceState.bind(history);
|
|
163
|
+
history.pushState = (...args) => {
|
|
164
|
+
const r = push(...args);
|
|
165
|
+
queueMicrotask(run);
|
|
166
|
+
return r;
|
|
167
|
+
};
|
|
168
|
+
history.replaceState = (...args) => {
|
|
169
|
+
const r = replace(...args);
|
|
170
|
+
queueMicrotask(run);
|
|
171
|
+
return r;
|
|
172
|
+
};
|
|
173
|
+
return () => {
|
|
174
|
+
window.removeEventListener("popstate", run);
|
|
175
|
+
history.pushState = push;
|
|
176
|
+
history.replaceState = replace;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
track,
|
|
181
|
+
trackPageView,
|
|
182
|
+
trackFormSubmit,
|
|
183
|
+
trackCallClick,
|
|
184
|
+
subscribeToSpaNavigation
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
188
|
+
0 && (module.exports = {
|
|
189
|
+
createJmpAnalytics
|
|
190
|
+
});
|
|
191
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type JmpEventType = \"page_view\" | \"form_submit\" | \"call_click\"\n\nexport type JmpAnalyticsConfig = {\n /** Dashboard origin, e.g. https://analytics.example.com (no trailing slash) */\n baseUrl: string\n /** Per-website key from JMP admin */\n trackingKey: string\n /** Log requests to console */\n debug?: boolean\n /** Override fetch (tests, React Native) */\n fetchImpl?: typeof fetch\n /**\n * Suppress duplicate `page_view` for the same path within this window (ms).\n * Helps with React Strict Mode double-mount; typical: 500–1000.\n */\n pageViewDedupeMs?: number\n}\n\nexport type TrackPageViewOptions = {\n path?: string\n source?: string\n device?: string\n country?: string\n metadata?: Record<string, unknown>\n}\n\nconst DEFAULT_SOURCE = \"direct\"\nconst SESSION_STORAGE_KEY = \"jmp_analytics_sid\"\n\nfunction normalizeBaseUrl(baseUrl: string) {\n return baseUrl.replace(/\\/$/, \"\")\n}\n\nfunction inferDevice(): string {\n if (typeof window === \"undefined\") {\n return \"unknown\"\n }\n return window.innerWidth < 768 ? \"mobile\" : \"desktop\"\n}\n\n/** Stable per-tab session id for bounce and unique session metrics (server-stored). */\nfunction getOrCreateSessionId(): string | undefined {\n if (typeof window === \"undefined\" || !globalThis.crypto?.randomUUID) {\n return undefined\n }\n try {\n let id = sessionStorage.getItem(SESSION_STORAGE_KEY)\n if (!id) {\n id = crypto.randomUUID()\n sessionStorage.setItem(SESSION_STORAGE_KEY, id)\n }\n return id\n } catch {\n return undefined\n }\n}\n\n/**\n * Create a tracker instance. Safe to call once per app and reuse.\n */\nexport function createJmpAnalytics(config: JmpAnalyticsConfig) {\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const resolvedFetch =\n config.fetchImpl ??\n (typeof globalThis !== \"undefined\" && globalThis.fetch\n ? globalThis.fetch.bind(globalThis)\n : undefined)\n\n if (!resolvedFetch) {\n throw new Error(\n \"createJmpAnalytics: fetch is not available; pass fetchImpl in config\"\n )\n }\n\n const fetchFn: typeof fetch = resolvedFetch\n\n let lastDedupePath = \"\"\n let lastDedupeAt = 0\n\n async function send(body: Record<string, unknown>) {\n const url = `${baseUrl}/api/events`\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-JMP-Tracking-Key\": config.trackingKey,\n }\n\n const sid = getOrCreateSessionId()\n const payload = { ...body, ...(sid ? { sessionId: sid } : {}) }\n\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"POST\", url, payload)\n }\n\n const res = await fetchFn(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(payload),\n keepalive: true,\n })\n\n if (!res.ok) {\n let detail = \"\"\n try {\n const err = (await res.json()) as { error?: string }\n detail = err.error ?? (await res.text())\n } catch {\n detail = await res.text()\n }\n throw new Error(`JMP Analytics HTTP ${res.status}: ${detail}`)\n }\n }\n\n function shouldDedupePageView(path: string): boolean {\n const ms = config.pageViewDedupeMs ?? 0\n if (ms <= 0 || typeof window === \"undefined\") {\n return false\n }\n const now = Date.now()\n if (path === lastDedupePath && now - lastDedupeAt < ms) {\n return true\n }\n lastDedupePath = path\n lastDedupeAt = now\n return false\n }\n\n function trackPageView(options?: TrackPageViewOptions) {\n const path =\n options?.path ??\n (typeof window !== \"undefined\" ? window.location.pathname : \"/\")\n\n if (shouldDedupePageView(path)) {\n return Promise.resolve()\n }\n\n return send({\n type: \"page_view\",\n path: path || \"/\",\n source: options?.source ?? DEFAULT_SOURCE,\n device: options?.device ?? inferDevice(),\n country: options?.country,\n metadata:\n options?.metadata && typeof options.metadata === \"object\"\n ? options.metadata\n : {},\n })\n }\n\n function trackFormSubmit(\n path: string,\n metadata?: Record<string, unknown>\n ) {\n return send({\n type: \"form_submit\",\n path: path || \"/\",\n metadata: metadata ?? {},\n })\n }\n\n function trackCallClick(\n path: string,\n metadata?: Record<string, unknown>\n ) {\n return send({\n type: \"call_click\",\n path: path || \"/\",\n metadata: metadata ?? {},\n })\n }\n\n function track(\n type: JmpEventType,\n payload: {\n path: string\n metadata?: Record<string, unknown>\n source?: string\n device?: string\n country?: string\n }\n ) {\n if (type === \"page_view\") {\n return trackPageView({\n path: payload.path,\n source: payload.source,\n device: payload.device,\n country: payload.country,\n metadata: payload.metadata,\n })\n }\n if (type === \"form_submit\") {\n return trackFormSubmit(payload.path, payload.metadata)\n }\n return trackCallClick(payload.path, payload.metadata)\n }\n\n /**\n * Fire `page_view` on browser back/forward and when `history.pushState` /\n * `replaceState` are used (many SPAs). Call once after creating the tracker.\n * Returns an unsubscribe function.\n */\n function subscribeToSpaNavigation(): () => void {\n if (typeof window === \"undefined\") {\n return () => {}\n }\n\n let lastPath = window.location.pathname\n\n const run = () => {\n const path = window.location.pathname\n if (path !== lastPath) {\n lastPath = path\n void trackPageView({ path })\n }\n }\n\n window.addEventListener(\"popstate\", run)\n\n const push = history.pushState.bind(history)\n const replace = history.replaceState.bind(history)\n\n history.pushState = (...args: Parameters<History[\"pushState\"]>) => {\n const r = push(...args)\n queueMicrotask(run)\n return r\n }\n history.replaceState = (...args: Parameters<History[\"replaceState\"]>) => {\n const r = replace(...args)\n queueMicrotask(run)\n return r\n }\n\n return () => {\n window.removeEventListener(\"popstate\", run)\n history.pushState = push\n history.replaceState = replace\n }\n }\n\n return {\n track,\n trackPageView,\n trackFormSubmit,\n trackCallClick,\n subscribeToSpaNavigation,\n }\n}\n\nexport type JmpAnalytics = ReturnType<typeof createJmpAnalytics>\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
type JmpEventType = "page_view" | "form_submit" | "call_click";
|
|
2
|
+
type JmpAnalyticsConfig = {
|
|
3
|
+
/** Dashboard origin, e.g. https://analytics.example.com (no trailing slash) */
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
/** Per-website key from JMP admin */
|
|
6
|
+
trackingKey: string;
|
|
7
|
+
/** Log requests to console */
|
|
8
|
+
debug?: boolean;
|
|
9
|
+
/** Override fetch (tests, React Native) */
|
|
10
|
+
fetchImpl?: typeof fetch;
|
|
11
|
+
/**
|
|
12
|
+
* Suppress duplicate `page_view` for the same path within this window (ms).
|
|
13
|
+
* Helps with React Strict Mode double-mount; typical: 500–1000.
|
|
14
|
+
*/
|
|
15
|
+
pageViewDedupeMs?: number;
|
|
16
|
+
};
|
|
17
|
+
type TrackPageViewOptions = {
|
|
18
|
+
path?: string;
|
|
19
|
+
source?: string;
|
|
20
|
+
device?: string;
|
|
21
|
+
country?: string;
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Create a tracker instance. Safe to call once per app and reuse.
|
|
26
|
+
*/
|
|
27
|
+
declare function createJmpAnalytics(config: JmpAnalyticsConfig): {
|
|
28
|
+
track: (type: JmpEventType, payload: {
|
|
29
|
+
path: string;
|
|
30
|
+
metadata?: Record<string, unknown>;
|
|
31
|
+
source?: string;
|
|
32
|
+
device?: string;
|
|
33
|
+
country?: string;
|
|
34
|
+
}) => Promise<void>;
|
|
35
|
+
trackPageView: (options?: TrackPageViewOptions) => Promise<void>;
|
|
36
|
+
trackFormSubmit: (path: string, metadata?: Record<string, unknown>) => Promise<void>;
|
|
37
|
+
trackCallClick: (path: string, metadata?: Record<string, unknown>) => Promise<void>;
|
|
38
|
+
subscribeToSpaNavigation: () => () => void;
|
|
39
|
+
};
|
|
40
|
+
type JmpAnalytics = ReturnType<typeof createJmpAnalytics>;
|
|
41
|
+
|
|
42
|
+
export { type JmpAnalytics, type JmpAnalyticsConfig, type JmpEventType, type TrackPageViewOptions, createJmpAnalytics };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
type JmpEventType = "page_view" | "form_submit" | "call_click";
|
|
2
|
+
type JmpAnalyticsConfig = {
|
|
3
|
+
/** Dashboard origin, e.g. https://analytics.example.com (no trailing slash) */
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
/** Per-website key from JMP admin */
|
|
6
|
+
trackingKey: string;
|
|
7
|
+
/** Log requests to console */
|
|
8
|
+
debug?: boolean;
|
|
9
|
+
/** Override fetch (tests, React Native) */
|
|
10
|
+
fetchImpl?: typeof fetch;
|
|
11
|
+
/**
|
|
12
|
+
* Suppress duplicate `page_view` for the same path within this window (ms).
|
|
13
|
+
* Helps with React Strict Mode double-mount; typical: 500–1000.
|
|
14
|
+
*/
|
|
15
|
+
pageViewDedupeMs?: number;
|
|
16
|
+
};
|
|
17
|
+
type TrackPageViewOptions = {
|
|
18
|
+
path?: string;
|
|
19
|
+
source?: string;
|
|
20
|
+
device?: string;
|
|
21
|
+
country?: string;
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Create a tracker instance. Safe to call once per app and reuse.
|
|
26
|
+
*/
|
|
27
|
+
declare function createJmpAnalytics(config: JmpAnalyticsConfig): {
|
|
28
|
+
track: (type: JmpEventType, payload: {
|
|
29
|
+
path: string;
|
|
30
|
+
metadata?: Record<string, unknown>;
|
|
31
|
+
source?: string;
|
|
32
|
+
device?: string;
|
|
33
|
+
country?: string;
|
|
34
|
+
}) => Promise<void>;
|
|
35
|
+
trackPageView: (options?: TrackPageViewOptions) => Promise<void>;
|
|
36
|
+
trackFormSubmit: (path: string, metadata?: Record<string, unknown>) => Promise<void>;
|
|
37
|
+
trackCallClick: (path: string, metadata?: Record<string, unknown>) => Promise<void>;
|
|
38
|
+
subscribeToSpaNavigation: () => () => void;
|
|
39
|
+
};
|
|
40
|
+
type JmpAnalytics = ReturnType<typeof createJmpAnalytics>;
|
|
41
|
+
|
|
42
|
+
export { type JmpAnalytics, type JmpAnalyticsConfig, type JmpEventType, type TrackPageViewOptions, createJmpAnalytics };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var DEFAULT_SOURCE = "direct";
|
|
3
|
+
var SESSION_STORAGE_KEY = "jmp_analytics_sid";
|
|
4
|
+
function normalizeBaseUrl(baseUrl) {
|
|
5
|
+
return baseUrl.replace(/\/$/, "");
|
|
6
|
+
}
|
|
7
|
+
function inferDevice() {
|
|
8
|
+
if (typeof window === "undefined") {
|
|
9
|
+
return "unknown";
|
|
10
|
+
}
|
|
11
|
+
return window.innerWidth < 768 ? "mobile" : "desktop";
|
|
12
|
+
}
|
|
13
|
+
function getOrCreateSessionId() {
|
|
14
|
+
if (typeof window === "undefined" || !globalThis.crypto?.randomUUID) {
|
|
15
|
+
return void 0;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
let id = sessionStorage.getItem(SESSION_STORAGE_KEY);
|
|
19
|
+
if (!id) {
|
|
20
|
+
id = crypto.randomUUID();
|
|
21
|
+
sessionStorage.setItem(SESSION_STORAGE_KEY, id);
|
|
22
|
+
}
|
|
23
|
+
return id;
|
|
24
|
+
} catch {
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function createJmpAnalytics(config) {
|
|
29
|
+
const baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
30
|
+
const resolvedFetch = config.fetchImpl ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
31
|
+
if (!resolvedFetch) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"createJmpAnalytics: fetch is not available; pass fetchImpl in config"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const fetchFn = resolvedFetch;
|
|
37
|
+
let lastDedupePath = "";
|
|
38
|
+
let lastDedupeAt = 0;
|
|
39
|
+
async function send(body) {
|
|
40
|
+
const url = `${baseUrl}/api/events`;
|
|
41
|
+
const headers = {
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
"X-JMP-Tracking-Key": config.trackingKey
|
|
44
|
+
};
|
|
45
|
+
const sid = getOrCreateSessionId();
|
|
46
|
+
const payload = { ...body, ...sid ? { sessionId: sid } : {} };
|
|
47
|
+
if (config.debug) {
|
|
48
|
+
console.debug("[@jmp-technologies/analytics]", "POST", url, payload);
|
|
49
|
+
}
|
|
50
|
+
const res = await fetchFn(url, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers,
|
|
53
|
+
body: JSON.stringify(payload),
|
|
54
|
+
keepalive: true
|
|
55
|
+
});
|
|
56
|
+
if (!res.ok) {
|
|
57
|
+
let detail = "";
|
|
58
|
+
try {
|
|
59
|
+
const err = await res.json();
|
|
60
|
+
detail = err.error ?? await res.text();
|
|
61
|
+
} catch {
|
|
62
|
+
detail = await res.text();
|
|
63
|
+
}
|
|
64
|
+
throw new Error(`JMP Analytics HTTP ${res.status}: ${detail}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function shouldDedupePageView(path) {
|
|
68
|
+
const ms = config.pageViewDedupeMs ?? 0;
|
|
69
|
+
if (ms <= 0 || typeof window === "undefined") {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
if (path === lastDedupePath && now - lastDedupeAt < ms) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
lastDedupePath = path;
|
|
77
|
+
lastDedupeAt = now;
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
function trackPageView(options) {
|
|
81
|
+
const path = options?.path ?? (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
82
|
+
if (shouldDedupePageView(path)) {
|
|
83
|
+
return Promise.resolve();
|
|
84
|
+
}
|
|
85
|
+
return send({
|
|
86
|
+
type: "page_view",
|
|
87
|
+
path: path || "/",
|
|
88
|
+
source: options?.source ?? DEFAULT_SOURCE,
|
|
89
|
+
device: options?.device ?? inferDevice(),
|
|
90
|
+
country: options?.country,
|
|
91
|
+
metadata: options?.metadata && typeof options.metadata === "object" ? options.metadata : {}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function trackFormSubmit(path, metadata) {
|
|
95
|
+
return send({
|
|
96
|
+
type: "form_submit",
|
|
97
|
+
path: path || "/",
|
|
98
|
+
metadata: metadata ?? {}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function trackCallClick(path, metadata) {
|
|
102
|
+
return send({
|
|
103
|
+
type: "call_click",
|
|
104
|
+
path: path || "/",
|
|
105
|
+
metadata: metadata ?? {}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function track(type, payload) {
|
|
109
|
+
if (type === "page_view") {
|
|
110
|
+
return trackPageView({
|
|
111
|
+
path: payload.path,
|
|
112
|
+
source: payload.source,
|
|
113
|
+
device: payload.device,
|
|
114
|
+
country: payload.country,
|
|
115
|
+
metadata: payload.metadata
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (type === "form_submit") {
|
|
119
|
+
return trackFormSubmit(payload.path, payload.metadata);
|
|
120
|
+
}
|
|
121
|
+
return trackCallClick(payload.path, payload.metadata);
|
|
122
|
+
}
|
|
123
|
+
function subscribeToSpaNavigation() {
|
|
124
|
+
if (typeof window === "undefined") {
|
|
125
|
+
return () => {
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
let lastPath = window.location.pathname;
|
|
129
|
+
const run = () => {
|
|
130
|
+
const path = window.location.pathname;
|
|
131
|
+
if (path !== lastPath) {
|
|
132
|
+
lastPath = path;
|
|
133
|
+
void trackPageView({ path });
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
window.addEventListener("popstate", run);
|
|
137
|
+
const push = history.pushState.bind(history);
|
|
138
|
+
const replace = history.replaceState.bind(history);
|
|
139
|
+
history.pushState = (...args) => {
|
|
140
|
+
const r = push(...args);
|
|
141
|
+
queueMicrotask(run);
|
|
142
|
+
return r;
|
|
143
|
+
};
|
|
144
|
+
history.replaceState = (...args) => {
|
|
145
|
+
const r = replace(...args);
|
|
146
|
+
queueMicrotask(run);
|
|
147
|
+
return r;
|
|
148
|
+
};
|
|
149
|
+
return () => {
|
|
150
|
+
window.removeEventListener("popstate", run);
|
|
151
|
+
history.pushState = push;
|
|
152
|
+
history.replaceState = replace;
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
track,
|
|
157
|
+
trackPageView,
|
|
158
|
+
trackFormSubmit,
|
|
159
|
+
trackCallClick,
|
|
160
|
+
subscribeToSpaNavigation
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
export {
|
|
164
|
+
createJmpAnalytics
|
|
165
|
+
};
|
|
166
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type JmpEventType = \"page_view\" | \"form_submit\" | \"call_click\"\n\nexport type JmpAnalyticsConfig = {\n /** Dashboard origin, e.g. https://analytics.example.com (no trailing slash) */\n baseUrl: string\n /** Per-website key from JMP admin */\n trackingKey: string\n /** Log requests to console */\n debug?: boolean\n /** Override fetch (tests, React Native) */\n fetchImpl?: typeof fetch\n /**\n * Suppress duplicate `page_view` for the same path within this window (ms).\n * Helps with React Strict Mode double-mount; typical: 500–1000.\n */\n pageViewDedupeMs?: number\n}\n\nexport type TrackPageViewOptions = {\n path?: string\n source?: string\n device?: string\n country?: string\n metadata?: Record<string, unknown>\n}\n\nconst DEFAULT_SOURCE = \"direct\"\nconst SESSION_STORAGE_KEY = \"jmp_analytics_sid\"\n\nfunction normalizeBaseUrl(baseUrl: string) {\n return baseUrl.replace(/\\/$/, \"\")\n}\n\nfunction inferDevice(): string {\n if (typeof window === \"undefined\") {\n return \"unknown\"\n }\n return window.innerWidth < 768 ? \"mobile\" : \"desktop\"\n}\n\n/** Stable per-tab session id for bounce and unique session metrics (server-stored). */\nfunction getOrCreateSessionId(): string | undefined {\n if (typeof window === \"undefined\" || !globalThis.crypto?.randomUUID) {\n return undefined\n }\n try {\n let id = sessionStorage.getItem(SESSION_STORAGE_KEY)\n if (!id) {\n id = crypto.randomUUID()\n sessionStorage.setItem(SESSION_STORAGE_KEY, id)\n }\n return id\n } catch {\n return undefined\n }\n}\n\n/**\n * Create a tracker instance. Safe to call once per app and reuse.\n */\nexport function createJmpAnalytics(config: JmpAnalyticsConfig) {\n const baseUrl = normalizeBaseUrl(config.baseUrl)\n const resolvedFetch =\n config.fetchImpl ??\n (typeof globalThis !== \"undefined\" && globalThis.fetch\n ? globalThis.fetch.bind(globalThis)\n : undefined)\n\n if (!resolvedFetch) {\n throw new Error(\n \"createJmpAnalytics: fetch is not available; pass fetchImpl in config\"\n )\n }\n\n const fetchFn: typeof fetch = resolvedFetch\n\n let lastDedupePath = \"\"\n let lastDedupeAt = 0\n\n async function send(body: Record<string, unknown>) {\n const url = `${baseUrl}/api/events`\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-JMP-Tracking-Key\": config.trackingKey,\n }\n\n const sid = getOrCreateSessionId()\n const payload = { ...body, ...(sid ? { sessionId: sid } : {}) }\n\n if (config.debug) {\n console.debug(\"[@jmp-technologies/analytics]\", \"POST\", url, payload)\n }\n\n const res = await fetchFn(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(payload),\n keepalive: true,\n })\n\n if (!res.ok) {\n let detail = \"\"\n try {\n const err = (await res.json()) as { error?: string }\n detail = err.error ?? (await res.text())\n } catch {\n detail = await res.text()\n }\n throw new Error(`JMP Analytics HTTP ${res.status}: ${detail}`)\n }\n }\n\n function shouldDedupePageView(path: string): boolean {\n const ms = config.pageViewDedupeMs ?? 0\n if (ms <= 0 || typeof window === \"undefined\") {\n return false\n }\n const now = Date.now()\n if (path === lastDedupePath && now - lastDedupeAt < ms) {\n return true\n }\n lastDedupePath = path\n lastDedupeAt = now\n return false\n }\n\n function trackPageView(options?: TrackPageViewOptions) {\n const path =\n options?.path ??\n (typeof window !== \"undefined\" ? window.location.pathname : \"/\")\n\n if (shouldDedupePageView(path)) {\n return Promise.resolve()\n }\n\n return send({\n type: \"page_view\",\n path: path || \"/\",\n source: options?.source ?? DEFAULT_SOURCE,\n device: options?.device ?? inferDevice(),\n country: options?.country,\n metadata:\n options?.metadata && typeof options.metadata === \"object\"\n ? options.metadata\n : {},\n })\n }\n\n function trackFormSubmit(\n path: string,\n metadata?: Record<string, unknown>\n ) {\n return send({\n type: \"form_submit\",\n path: path || \"/\",\n metadata: metadata ?? {},\n })\n }\n\n function trackCallClick(\n path: string,\n metadata?: Record<string, unknown>\n ) {\n return send({\n type: \"call_click\",\n path: path || \"/\",\n metadata: metadata ?? {},\n })\n }\n\n function track(\n type: JmpEventType,\n payload: {\n path: string\n metadata?: Record<string, unknown>\n source?: string\n device?: string\n country?: string\n }\n ) {\n if (type === \"page_view\") {\n return trackPageView({\n path: payload.path,\n source: payload.source,\n device: payload.device,\n country: payload.country,\n metadata: payload.metadata,\n })\n }\n if (type === \"form_submit\") {\n return trackFormSubmit(payload.path, payload.metadata)\n }\n return trackCallClick(payload.path, payload.metadata)\n }\n\n /**\n * Fire `page_view` on browser back/forward and when `history.pushState` /\n * `replaceState` are used (many SPAs). Call once after creating the tracker.\n * Returns an unsubscribe function.\n */\n function subscribeToSpaNavigation(): () => void {\n if (typeof window === \"undefined\") {\n return () => {}\n }\n\n let lastPath = window.location.pathname\n\n const run = () => {\n const path = window.location.pathname\n if (path !== lastPath) {\n lastPath = path\n void trackPageView({ path })\n }\n }\n\n window.addEventListener(\"popstate\", run)\n\n const push = history.pushState.bind(history)\n const replace = history.replaceState.bind(history)\n\n history.pushState = (...args: Parameters<History[\"pushState\"]>) => {\n const r = push(...args)\n queueMicrotask(run)\n return r\n }\n history.replaceState = (...args: Parameters<History[\"replaceState\"]>) => {\n const r = replace(...args)\n queueMicrotask(run)\n return r\n }\n\n return () => {\n window.removeEventListener(\"popstate\", run)\n history.pushState = push\n history.replaceState = replace\n }\n }\n\n return {\n track,\n trackPageView,\n trackFormSubmit,\n trackCallClick,\n subscribeToSpaNavigation,\n }\n}\n\nexport type JmpAnalytics = ReturnType<typeof createJmpAnalytics>\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jmp-technologies/analytics",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Browser client for JMP Analytics event ingestion",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": ["dist"],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean --sourcemap"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"tsup": "^8.5.1",
|
|
26
|
+
"typescript": "^5.9.3"
|
|
27
|
+
}
|
|
28
|
+
}
|