onedollarstats 0.0.8 → 0.0.9
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/index.d.ts +1 -0
- package/dist/index.js +118 -7
- package/dist/index.js.map +3 -3
- package/dist/utils/create-modal.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AnalyticsConfig, BaseProps } from "./types";
|
|
2
|
+
export declare const defaultConfig: Required<AnalyticsConfig>;
|
|
2
3
|
export declare const configure: (userConfig?: AnalyticsConfig) => void;
|
|
3
4
|
export declare const event: (eventName: string, pathOrProps?: string | BaseProps, props?: BaseProps) => Promise<void>;
|
|
4
5
|
export declare const view: (pathOrProps?: string | BaseProps, props?: BaseProps) => Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,103 @@
|
|
|
1
|
+
// src/utils/create-modal.ts
|
|
2
|
+
var createDebugModal = (debugUrl, analyticsUrl) => {
|
|
3
|
+
if (!document.getElementById("onedollatstats-modal-styles")) {
|
|
4
|
+
const style = document.createElement("style");
|
|
5
|
+
style.id = "onedollatstats-modal-styles";
|
|
6
|
+
style.textContent = CSS;
|
|
7
|
+
document.head.appendChild(style);
|
|
8
|
+
}
|
|
9
|
+
const modal = document.createElement("div");
|
|
10
|
+
modal.className = "dev-modal";
|
|
11
|
+
modal.innerHTML = `
|
|
12
|
+
<button class="close-btn">×</button>
|
|
13
|
+
<p class="title">onedollarstats debug window</p>
|
|
14
|
+
<p>${icons.info}<span class="text">Tracking localhost as ${debugUrl}</span></p>
|
|
15
|
+
<div id="event-log" style="max-height: 100px; overflow-y: auto;"></div>
|
|
16
|
+
`;
|
|
17
|
+
document.body.appendChild(modal);
|
|
18
|
+
modal.querySelector(".close-btn")?.addEventListener("click", () => modal.remove(), { once: true });
|
|
19
|
+
if (analyticsUrl === defaultConfig.collectorUrl) {
|
|
20
|
+
const img = new Image(1, 1);
|
|
21
|
+
img.onerror = () => {
|
|
22
|
+
if (modal.querySelector("#ad-blocker-warning")) return;
|
|
23
|
+
const warning = document.createElement("p");
|
|
24
|
+
warning.id = "ad-blocker-warning";
|
|
25
|
+
warning.innerHTML = `${icons.warning}<span class="text">Health check failed - ad blocker might be interfering.</span>`;
|
|
26
|
+
modal.querySelector(".title")?.insertAdjacentElement("afterend", warning);
|
|
27
|
+
};
|
|
28
|
+
img.src = "https://collector.onedollarstats.com/pixel-health";
|
|
29
|
+
}
|
|
30
|
+
return (message, success) => {
|
|
31
|
+
const logContainer = modal.querySelector("#event-log");
|
|
32
|
+
if (!logContainer || modal.querySelector("#ad-blocker-warning")) return;
|
|
33
|
+
const entry = document.createElement("p");
|
|
34
|
+
entry.innerHTML = `${success ? icons.success : icons.error}<span class="text">${message}</span>`;
|
|
35
|
+
logContainer.appendChild(entry);
|
|
36
|
+
logContainer.scrollTop = logContainer.scrollHeight;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
var icons = {
|
|
40
|
+
info: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="gray" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>`,
|
|
41
|
+
success: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="green" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>`,
|
|
42
|
+
error: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="red" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`,
|
|
43
|
+
warning: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="orange" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>`
|
|
44
|
+
};
|
|
45
|
+
var CSS = `
|
|
46
|
+
.dev-modal {
|
|
47
|
+
position: fixed;
|
|
48
|
+
bottom: 20px;
|
|
49
|
+
right: 20px;
|
|
50
|
+
background: #fff;
|
|
51
|
+
padding: 14px;
|
|
52
|
+
border-radius: 8px;
|
|
53
|
+
max-width: 340px;
|
|
54
|
+
max-height: 180px;
|
|
55
|
+
overflow-y: auto;
|
|
56
|
+
box-shadow: 0 5px 20px rgba(0,0,0,0.3);
|
|
57
|
+
font-family: sans-serif;
|
|
58
|
+
z-index: 99999;
|
|
59
|
+
animation: slideIn 0.3s ease-out;
|
|
60
|
+
}
|
|
61
|
+
.dev-modal .title {
|
|
62
|
+
text-transform: uppercase;
|
|
63
|
+
font-size: 11px;
|
|
64
|
+
font-weight: 500;
|
|
65
|
+
margin: 0 0 6px 0;
|
|
66
|
+
letter-spacing: 0.5px;
|
|
67
|
+
}
|
|
68
|
+
.dev-modal p {
|
|
69
|
+
margin: 4px 0;
|
|
70
|
+
font-size: 14px;
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: flex-start;
|
|
73
|
+
gap: 4px;
|
|
74
|
+
}
|
|
75
|
+
.dev-modal .text {
|
|
76
|
+
word-break: break-word;
|
|
77
|
+
}
|
|
78
|
+
.dev-modal p svg {
|
|
79
|
+
flex-shrink: 0;
|
|
80
|
+
width: 18px;
|
|
81
|
+
height: 18px;
|
|
82
|
+
margin-top: 1px;
|
|
83
|
+
}
|
|
84
|
+
.dev-modal .close-btn {
|
|
85
|
+
position: absolute;
|
|
86
|
+
top: 2px;
|
|
87
|
+
right: 8px;
|
|
88
|
+
background: transparent;
|
|
89
|
+
border: none;
|
|
90
|
+
cursor: pointer;
|
|
91
|
+
font-size: 14px;
|
|
92
|
+
font-weight: bold;
|
|
93
|
+
color: #333;
|
|
94
|
+
}
|
|
95
|
+
@keyframes slideIn {
|
|
96
|
+
from { opacity: 0; transform: translateX(100%); }
|
|
97
|
+
to { opacity: 1; transform: translateX(0); }
|
|
98
|
+
}
|
|
99
|
+
`;
|
|
100
|
+
|
|
1
101
|
// src/utils/environment.ts
|
|
2
102
|
var getEnvironment = () => ({
|
|
3
103
|
isLocalhost: /^localhost$|^127(\.[0-9]+){0,2}\.[0-9]+$|^\[::1?\]$/.test(location.hostname) || location.protocol === "file:",
|
|
@@ -48,8 +148,7 @@ var resolvePath = (pathOrProps) => {
|
|
|
48
148
|
if (pathOrProps) return pathOrProps;
|
|
49
149
|
const sources = [
|
|
50
150
|
{ value: document.body?.getAttribute("data-s-path"), name: "data-s-path" },
|
|
51
|
-
{ value: document.body?.getAttribute("data-s:path"), name: "data-s:path" }
|
|
52
|
-
{ value: document.querySelector('meta[name="stonks-path"]')?.getAttribute("content"), name: "meta[name='stonks-path']" }
|
|
151
|
+
{ value: document.body?.getAttribute("data-s:path"), name: "data-s:path" }
|
|
53
152
|
];
|
|
54
153
|
const existing = sources.filter(({ value }) => value);
|
|
55
154
|
if (existing.length > 1) {
|
|
@@ -82,12 +181,15 @@ var _AnalyticsTracker = class _AnalyticsTracker {
|
|
|
82
181
|
constructor(userConfig = {}) {
|
|
83
182
|
this.autocollectSetupDone = false;
|
|
84
183
|
this.lastPage = null;
|
|
184
|
+
this.modalLog = () => {
|
|
185
|
+
};
|
|
85
186
|
this.config = { ...defaultConfig, ...userConfig };
|
|
86
187
|
if (!isClient()) return;
|
|
87
188
|
const { isLocalhost } = getEnvironment();
|
|
88
189
|
if (isLocalhost && this.config.trackLocalhostAs) {
|
|
89
190
|
console.log(`[onedollarstats]
|
|
90
191
|
OneDollarStats successfully connected! Tracking your localhost as ${this.config.trackLocalhostAs}`);
|
|
192
|
+
this.modalLog = createDebugModal(this.config.trackLocalhostAs, this.config.collectorUrl);
|
|
91
193
|
}
|
|
92
194
|
if (this.config.autocollect) this.setupAutocollect();
|
|
93
195
|
}
|
|
@@ -98,14 +200,20 @@ OneDollarStats successfully connected! Tracking your localhost as ${this.config.
|
|
|
98
200
|
}
|
|
99
201
|
return _AnalyticsTracker.instance;
|
|
100
202
|
}
|
|
101
|
-
async sendWithBeaconOrFetch(stringifiedBody) {
|
|
102
|
-
if (navigator.sendBeacon?.(this.config.collectorUrl, stringifiedBody))
|
|
203
|
+
async sendWithBeaconOrFetch(stringifiedBody, callback) {
|
|
204
|
+
if (navigator.sendBeacon?.(this.config.collectorUrl, stringifiedBody)) {
|
|
205
|
+
callback(true);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
103
208
|
fetch(this.config.collectorUrl, {
|
|
104
209
|
method: "POST",
|
|
105
210
|
body: stringifiedBody,
|
|
106
211
|
headers: { "Content-Type": "application/json" },
|
|
107
212
|
keepalive: true
|
|
108
|
-
}).
|
|
213
|
+
}).then(({ ok }) => callback(ok)).catch((err) => {
|
|
214
|
+
console.error("[onedollarstats] fetch() failed:", err.message);
|
|
215
|
+
callback(false);
|
|
216
|
+
});
|
|
109
217
|
}
|
|
110
218
|
// Handles localhost replacement, referrer, UTM parameters, and debug mode.
|
|
111
219
|
// Uses img beacon then `navigator.sendBeacon` if available, otherwise falls back to `fetch`.
|
|
@@ -167,11 +275,13 @@ UTM: ${data.utm}`;
|
|
|
167
275
|
const payloadBase64 = btoa(stringifiedBody);
|
|
168
276
|
const safeGetThreshold = 1500;
|
|
169
277
|
const tryImageBeacon = payloadBase64.length <= safeGetThreshold;
|
|
278
|
+
const onComplete = (success) => this.modalLog(`${data.type} ${success ? "sent" : "failed to send"}`, success);
|
|
170
279
|
if (tryImageBeacon) {
|
|
171
280
|
const img = new Image(1, 1);
|
|
172
|
-
img.
|
|
281
|
+
img.onload = () => onComplete(true);
|
|
282
|
+
img.onerror = () => this.sendWithBeaconOrFetch(stringifiedBody, onComplete);
|
|
173
283
|
img.src = `${this.config.collectorUrl}?data=${payloadBase64}`;
|
|
174
|
-
} else await this.sendWithBeaconOrFetch(stringifiedBody);
|
|
284
|
+
} else await this.sendWithBeaconOrFetch(stringifiedBody, onComplete);
|
|
175
285
|
}
|
|
176
286
|
// Prevents duplicate pageviews and respects include/exclude page rules. Automatically parses UTM parameters from URL.
|
|
177
287
|
trackPageView({ path, props }, checkBlock = false) {
|
|
@@ -301,6 +411,7 @@ var view = async (pathOrProps, props) => {
|
|
|
301
411
|
};
|
|
302
412
|
export {
|
|
303
413
|
configure,
|
|
414
|
+
defaultConfig,
|
|
304
415
|
event,
|
|
305
416
|
view
|
|
306
417
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/utils/environment.ts", "../src/utils/parse-utm-params.ts", "../src/utils/props-parser.ts", "../src/utils/resolve-path.ts", "../src/utils/should-track.ts", "../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export const getEnvironment = (): {\n isLocalhost: boolean;\n isHeadlessBrowser: boolean;\n} => ({\n isLocalhost: /^localhost$|^127(\\.[0-9]+){0,2}\\.[0-9]+$|^\\[::1?\\]$/.test(location.hostname) || location.protocol === \"file:\",\n isHeadlessBrowser: Boolean(\n window.navigator.webdriver ||\n (\"_phantom\" in window && window._phantom) ||\n (\"__nightmare\" in window && window.__nightmare) ||\n (\"Cypress\" in window && window.Cypress)\n )\n});\nexport const isClient = (): boolean => {\n try {\n // Basic checks for window and document\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return false;\n\n // Check for navigator safely\n const ua = typeof navigator !== \"undefined\" ? navigator.userAgent : \"\";\n if (/node|jsdom/i.test(ua)) return false;\n return true;\n } catch {\n return false;\n }\n};\n", "export const parseUtmParams = (urlSearchParams: URLSearchParams) => {\n const utm: Record<string, string | string[]> = {};\n\n [\"utm_campaign\", \"utm_source\", \"utm_medium\", \"utm_term\", \"utm_content\"].forEach((key) => {\n const values = urlSearchParams.getAll(key);\n if (values.length === 1) {\n utm[key] = values[0];\n } else if (values.length > 1) {\n utm[key] = values; // store array if multiple values\n }\n });\n\n return utm;\n};\n", "export const parseProps = (propsString: string): Record<string, string> | undefined => {\n if (!propsString) return undefined;\n // \"key1=value1;key2=value2\"\n\n const splittedProps = propsString.split(\";\");\n const propsObj: Record<string, string> = {};\n\n for (const keyValueString of splittedProps) {\n const keyValuePair = keyValueString.split(\"=\").map((el) => el.trim());\n if (keyValuePair.length !== 2 || keyValuePair[0] === \"\" || keyValuePair[1] === \"\") continue;\n // @ts-ignore\n propsObj[keyValuePair[0]] = keyValuePair[1];\n }\n\n return Object.keys(propsObj).length === 0 ? undefined : propsObj;\n};\n", "export const resolvePath = (pathOrProps?: string): string => {\n if (pathOrProps) return pathOrProps;\n\n const sources = [\n { value: document.body?.getAttribute(\"data-s-path\"), name: \"data-s-path\" },\n { value: document.body?.getAttribute(\"data-s:path\"), name: \"data-s:path\" },\n { value: document.querySelector('meta[name=\"stonks-path\"]')?.getAttribute(\"content\"), name: \"meta[name='stonks-path']\" }\n ];\n\n // Only keep sources that actually exist\n const existing = sources.filter(({ value }) => value);\n\n if (existing.length > 1) {\n console.warn(\"[onedollarstats] Multiple path sources found. Using priority order:\", existing.map(({ name }) => name).join(\" > \"));\n }\n\n // Return first available value, fallback to location.pathname\n return existing[0]?.value ?? location.pathname;\n};\n", "import type { AnalyticsConfig } from \"../types\";\n\nconst matchesPattern = (path: string, pattern: string): boolean => {\n // Escape special regex characters except '*' which becomes '.*'\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*/g, \".*\");\n return new RegExp(`^${escaped}$`).test(path);\n};\n\nexport const shouldTrackPath = (path: string, config: Required<AnalyticsConfig>): boolean => {\n // Exclude pages first\n if (config.excludePages.some((pattern) => matchesPattern(path, pattern))) return false;\n // If includePages is defined, only allow matching paths\n if (config.includePages.length && !config.includePages.some((pattern) => matchesPattern(path, pattern))) return false;\n return true;\n};\n", "import type { AnalyticsConfig, BaseProps, BodyToSend, Event, ViewArguments } from \"./types\";\nimport { getEnvironment, isClient } from \"./utils/environment\";\nimport { parseUtmParams } from \"./utils/parse-utm-params\";\nimport { parseProps } from \"./utils/props-parser\";\nimport { resolvePath } from \"./utils/resolve-path\";\nimport { shouldTrackPath } from \"./utils/should-track\";\n\nconst defaultConfig: Required<AnalyticsConfig> = {\n trackLocalhostAs: null,\n collectorUrl: \"https://collector.onedollarstats.com/events\",\n hashRouting: false,\n autocollect: true,\n excludePages: [],\n includePages: []\n};\n\nclass AnalyticsTracker {\n private static instance: AnalyticsTracker | null = null;\n\n private autocollectSetupDone = false;\n private config: Required<AnalyticsConfig>;\n private lastPage: string | null = null;\n\n public static getInstance(userConfig: AnalyticsConfig = {}): AnalyticsTracker {\n // Fresh no-op instance for SSR\n if (!isClient()) return new AnalyticsTracker(userConfig);\n\n if (!AnalyticsTracker.instance) {\n AnalyticsTracker.instance = new AnalyticsTracker(userConfig);\n }\n return AnalyticsTracker.instance;\n }\n\n private constructor(userConfig: AnalyticsConfig = {}) {\n this.config = { ...defaultConfig, ...userConfig };\n\n // Skip setup in non-client environments\n if (!isClient()) return;\n\n const { isLocalhost } = getEnvironment();\n\n // Log connection only if on localhost and tracking is configured\n if (isLocalhost && this.config.trackLocalhostAs) {\n console.log(`[onedollarstats]\\nOneDollarStats successfully connected! Tracking your localhost as ${this.config.trackLocalhostAs}`);\n }\n\n // Auto-start autocollect\n if (this.config.autocollect) this.setupAutocollect();\n }\n\n private async sendWithBeaconOrFetch(stringifiedBody: string): Promise<void> {\n // First fallback: try sendBeacon\n if (navigator.sendBeacon?.(this.config.collectorUrl, stringifiedBody)) return;\n\n // Second fallback: use fetch() with keepalive\n fetch(this.config.collectorUrl, {\n method: \"POST\",\n body: stringifiedBody,\n headers: { \"Content-Type\": \"application/json\" },\n keepalive: true\n }).catch((err: Error) => console.error(\"[onedollarstats] fetch() failed:\", err.message));\n }\n\n // Handles localhost replacement, referrer, UTM parameters, and debug mode.\n // Uses img beacon then `navigator.sendBeacon` if available, otherwise falls back to `fetch`.\n private async send(data: Event): Promise<void> {\n const { isLocalhost, isHeadlessBrowser } = getEnvironment();\n if ((isLocalhost && !this.config.trackLocalhostAs) || isHeadlessBrowser) return;\n\n let urlToSend: URL = new URL(location.href);\n\n // Determine debug mode and handle localhost replacement\n let isDebug: boolean = false;\n if (isLocalhost && this.config.trackLocalhostAs) {\n try {\n const debugUrl = new URL(`https://${this.config.trackLocalhostAs}${urlToSend.pathname}`);\n if (urlToSend.hostname !== debugUrl.hostname) {\n isDebug = true;\n urlToSend = debugUrl;\n }\n } catch {\n return;\n }\n }\n\n // Clean query string unless UTM is explicitly provided\n urlToSend.search = \"\";\n if (data.path) urlToSend.pathname = data.path;\n\n const cleanUrl = urlToSend.href.replace(/\\/$/, \"\");\n\n // Determine referrer\n let referrer: string | undefined = data.referrer;\n try {\n if (!referrer && document.referrer && document.referrer !== \"null\") {\n const referrerURL = new URL(document.referrer);\n if (referrerURL.hostname !== urlToSend.hostname) referrer = referrerURL.href;\n }\n } catch {} // ignore malformed referrer\n\n // Build request body\n const body: BodyToSend = {\n u: cleanUrl,\n e: [\n {\n t: data.type,\n h: this.config.hashRouting,\n r: referrer,\n p: data.props\n }\n ]\n };\n\n if (data.utm && Object.keys(data.utm).length > 0) body.qs = data.utm;\n if (isDebug) {\n body.debug = true;\n let logMessage = `[onedollarstats]\\nEvent name: ${data.type}\\nEvent collected from: ${cleanUrl}`;\n if (data.props && Object.keys(data.props).length > 0) logMessage += `\\nProps: ${JSON.stringify(data.props, null, 2)}`;\n if (referrer) logMessage += `\\nReferrer: ${referrer}`;\n if (this.config.hashRouting) logMessage += `\\nHashRouting: ${this.config.hashRouting}`;\n if (data.utm && Object.keys(data.utm).length > 0) logMessage += `\\nUTM: ${data.utm}`;\n\n console.log(logMessage);\n }\n\n // Prepare the event payload\n const stringifiedBody = JSON.stringify(body);\n // Encode for safe inclusion in query string using Base64\n const payloadBase64 = btoa(stringifiedBody);\n\n const safeGetThreshold = 1500; // limit for query-string-containing URLs\n const tryImageBeacon = payloadBase64.length <= safeGetThreshold;\n\n if (tryImageBeacon) {\n // Send via image beacon\n const img = new Image(1, 1);\n\n // If loading image fails (server unavailable, blocked, etc.)\n img.onerror = () => this.sendWithBeaconOrFetch(stringifiedBody);\n\n // Primary attempt: send data via image beacon (GET request with query string)\n img.src = `${this.config.collectorUrl}?data=${payloadBase64}`;\n } else await this.sendWithBeaconOrFetch(stringifiedBody);\n }\n\n // Prevents duplicate pageviews and respects include/exclude page rules. Automatically parses UTM parameters from URL.\n private trackPageView({ path, props }: ViewArguments, checkBlock: boolean = false) {\n if (!isClient()) return;\n\n const viewPath = resolvePath(path);\n\n const viewProps =\n props ||\n (() => {\n const newProps = {};\n const elements = document.querySelectorAll(\"[data-s\\\\:view-props], [data-s-view-props]\");\n\n for (const el of Array.from(elements)) {\n const propsString = el.getAttribute(\"data-s-view-props\") || el.getAttribute(\"data-s:view-props\");\n if (!propsString) continue;\n const parsedProps = parseProps(propsString);\n Object.assign(newProps, parsedProps);\n }\n\n return Object.keys(newProps).length ? newProps : undefined;\n })();\n\n // Skip duplicate pageviews or excluded pages\n if (!this.config.hashRouting && this.lastPage === viewPath) return;\n\n // Skip page if checkBlock is true and the path should be excluded\n if (checkBlock && !shouldTrackPath(viewPath, this.config)) return;\n\n this.lastPage = viewPath;\n\n const utm = parseUtmParams(new URLSearchParams(location.search));\n this.send({ type: \"PageView\", path: viewPath, props: viewProps, utm });\n }\n\n /**\n * Tracks a custom event.\n * Can accept path string or a props object.\n *\n * @param eventName Name of the event to track.\n * @param pathOrProps Optional path string or props object.\n * @param props Optional props object if path string is provided.\n */\n public async event(eventName: string, pathOrProps?: string | BaseProps, props?: BaseProps) {\n if (!isClient()) return;\n\n const { isLocalhost, isHeadlessBrowser } = getEnvironment();\n if ((isLocalhost && !this.config.trackLocalhostAs) || isHeadlessBrowser) return;\n\n const args: ViewArguments = {};\n if (typeof pathOrProps === \"string\") {\n args.path = resolvePath(pathOrProps);\n\n args.props = props;\n } else if (typeof pathOrProps === \"object\") args.props = pathOrProps;\n\n this.send({ type: eventName, ...args });\n }\n\n /**\n * Records a page view.\n * Can accept path string or a props object.\n *\n * @param pathOrProps Optional path string or props object.\n * @param props Optional props when first arg is a path string.\n */\n public async view(pathOrProps?: string | BaseProps, props?: BaseProps) {\n if (!isClient()) return;\n\n const args: ViewArguments = {};\n\n if (typeof pathOrProps === \"string\") {\n args.path = pathOrProps;\n args.props = props;\n } else if (typeof pathOrProps === \"object\") {\n args.props = pathOrProps;\n }\n\n this.trackPageView(args);\n }\n\n /**\n * Installs global DOM/window listeners exactly once for:\n * - visibilitychange\n * - history.pushState\n * - popstate\n * - hashchange\n * - click autocapture for elements annotated with `data-s:event` & `data-s-event`\n *\n */\n private setupAutocollect() {\n if (!isClient() || this.autocollectSetupDone) return;\n this.autocollectSetupDone = true;\n\n const handlePageView = () => this.trackPageView({}, true);\n\n // visibilitychange\n const onVisibility = () => {\n if (document.visibilityState === \"visible\") handlePageView();\n };\n document.addEventListener(\"visibilitychange\", onVisibility);\n\n // pushState\n const origPush = history.pushState.bind(history);\n history.pushState = (...args) => {\n origPush(...args);\n requestAnimationFrame(() => {\n handlePageView();\n });\n };\n\n // popstate\n window.addEventListener(\"popstate\", handlePageView);\n\n // hashchange\n window.addEventListener(\"hashchange\", handlePageView);\n\n // click autocapture\n const onClick: EventListener = (ev: Event) => {\n const clickEvent = ev as MouseEvent;\n if (clickEvent.type === \"auxclick\" && clickEvent.button !== 1) return;\n\n const target = clickEvent.target as Element | null;\n if (!target) return;\n\n // Check if inside <a> or <button>\n const insideInteractive = !!target.closest(\"a, button\");\n\n let el: Element | null = target;\n let depth = 0;\n\n while (el) {\n const eventName = el.getAttribute(\"data-s-event\") || el.getAttribute(\"data-s:event\");\n if (eventName) {\n const propsAttr = el.getAttribute(\"data-s-event-props\") || el.getAttribute(\"data-s:event-props\");\n const props = propsAttr ? parseProps(propsAttr) : undefined;\n const path = el.getAttribute(\"data-s-event-path\") || el.getAttribute(\"data-s:event-path\") || undefined;\n\n if ((path && !shouldTrackPath(path, this.config)) || !shouldTrackPath(location.pathname, this.config)) {\n return;\n }\n\n this.event(eventName, path ?? props, props);\n return;\n }\n\n el = el.parentElement;\n depth++;\n\n // If not in <a>/<button>, stop after 3 levels\n if (!insideInteractive && depth >= 3) break;\n }\n };\n\n document.addEventListener(\"click\", onClick);\n\n // Fire initial pageview if already visible\n if (document.visibilityState === \"visible\") handlePageView();\n }\n}\n\nexport const configure = (userConfig: AnalyticsConfig = {}) => {\n AnalyticsTracker.getInstance(userConfig);\n};\n\nexport const event = async (eventName: string, pathOrProps?: string | BaseProps, props?: BaseProps) => {\n const instance = AnalyticsTracker.getInstance();\n await instance.event(eventName, pathOrProps, props);\n};\n\nexport const view = async (pathOrProps?: string | BaseProps, props?: BaseProps) => {\n const instance = AnalyticsTracker.getInstance();\n await instance.view(pathOrProps, props);\n};\n"],
|
|
5
|
-
"mappings": ";
|
|
3
|
+
"sources": ["../src/utils/create-modal.ts", "../src/utils/environment.ts", "../src/utils/parse-utm-params.ts", "../src/utils/props-parser.ts", "../src/utils/resolve-path.ts", "../src/utils/should-track.ts", "../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["import { defaultConfig } from \"..\";\n\nexport const createDebugModal = (debugUrl: string, analyticsUrl: string) => {\n if (!document.getElementById(\"onedollatstats-modal-styles\")) {\n const style = document.createElement(\"style\");\n style.id = \"onedollatstats-modal-styles\";\n style.textContent = CSS;\n document.head.appendChild(style);\n }\n\n // Create modal\n const modal = document.createElement(\"div\");\n modal.className = \"dev-modal\";\n modal.innerHTML = `\n <button class=\"close-btn\">×</button>\n <p class=\"title\">onedollarstats debug window</p>\n <p>${icons.info}<span class=\"text\">Tracking localhost as ${debugUrl}</span></p>\n <div id=\"event-log\" style=\"max-height: 100px; overflow-y: auto;\"></div>\n `;\n document.body.appendChild(modal);\n\n // Close handler\n modal.querySelector(\".close-btn\")?.addEventListener(\"click\", () => modal.remove(), { once: true });\n\n // Health check\n if (analyticsUrl === defaultConfig.collectorUrl) {\n const img = new Image(1, 1);\n img.onerror = () => {\n if (modal.querySelector(\"#ad-blocker-warning\")) return;\n const warning = document.createElement(\"p\");\n warning.id = \"ad-blocker-warning\";\n warning.innerHTML = `${icons.warning}<span class=\"text\">Health check failed - ad blocker might be interfering.</span>`;\n modal.querySelector(\".title\")?.insertAdjacentElement(\"afterend\", warning);\n };\n img.src = \"https://collector.onedollarstats.com/pixel-health\";\n }\n\n // Log rendering function\n return (message: string, success: boolean): void => {\n const logContainer = modal.querySelector(\"#event-log\");\n if (!logContainer || modal.querySelector(\"#ad-blocker-warning\")) return;\n\n const entry = document.createElement(\"p\");\n entry.innerHTML = `${success ? icons.success : icons.error}<span class=\"text\">${message}</span>`;\n logContainer.appendChild(entry);\n logContainer.scrollTop = logContainer.scrollHeight;\n };\n};\n\nconst icons = {\n info: `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"gray\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/></svg>`,\n success: `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"green\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"m9 12 2 2 4-4\"/></svg>`,\n error: `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"red\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"m15 9-6 6\"/><path d=\"m9 9 6 6\"/></svg>`,\n warning: `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"orange\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg>`\n} as const;\n\nconst CSS = `\n .dev-modal {\n position: fixed;\n bottom: 20px;\n right: 20px;\n background: #fff;\n padding: 14px;\n border-radius: 8px;\n max-width: 340px;\n max-height: 180px;\n overflow-y: auto;\n box-shadow: 0 5px 20px rgba(0,0,0,0.3);\n font-family: sans-serif;\n z-index: 99999;\n animation: slideIn 0.3s ease-out;\n }\n .dev-modal .title {\n text-transform: uppercase;\n font-size: 11px;\n font-weight: 500;\n margin: 0 0 6px 0;\n letter-spacing: 0.5px;\n }\n .dev-modal p { \n margin: 4px 0; \n font-size: 14px; \n display: flex; \n align-items: flex-start; \n gap: 4px; \n }\n .dev-modal .text {\n word-break: break-word;\n }\n .dev-modal p svg {\n flex-shrink: 0;\n width: 18px;\n height: 18px;\n margin-top: 1px;\n }\n .dev-modal .close-btn { \n position: absolute; \n top: 2px; \n right: 8px; \n background: transparent; \n border: none; \n cursor: pointer; \n font-size: 14px; \n font-weight: bold; \n color: #333; \n }\n @keyframes slideIn { \n from { opacity: 0; transform: translateX(100%); } \n to { opacity: 1; transform: translateX(0); } \n }\n`;\n", "export const getEnvironment = (): {\n isLocalhost: boolean;\n isHeadlessBrowser: boolean;\n} => ({\n isLocalhost: /^localhost$|^127(\\.[0-9]+){0,2}\\.[0-9]+$|^\\[::1?\\]$/.test(location.hostname) || location.protocol === \"file:\",\n isHeadlessBrowser: Boolean(\n window.navigator.webdriver ||\n (\"_phantom\" in window && window._phantom) ||\n (\"__nightmare\" in window && window.__nightmare) ||\n (\"Cypress\" in window && window.Cypress)\n )\n});\nexport const isClient = (): boolean => {\n try {\n // Basic checks for window and document\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return false;\n\n // Check for navigator safely\n const ua = typeof navigator !== \"undefined\" ? navigator.userAgent : \"\";\n if (/node|jsdom/i.test(ua)) return false;\n return true;\n } catch {\n return false;\n }\n};\n", "export const parseUtmParams = (urlSearchParams: URLSearchParams) => {\n const utm: Record<string, string | string[]> = {};\n\n [\"utm_campaign\", \"utm_source\", \"utm_medium\", \"utm_term\", \"utm_content\"].forEach((key) => {\n const values = urlSearchParams.getAll(key);\n if (values.length === 1) {\n utm[key] = values[0];\n } else if (values.length > 1) {\n utm[key] = values; // store array if multiple values\n }\n });\n\n return utm;\n};\n", "export const parseProps = (propsString: string): Record<string, string> | undefined => {\n if (!propsString) return undefined;\n // \"key1=value1;key2=value2\"\n\n const splittedProps = propsString.split(\";\");\n const propsObj: Record<string, string> = {};\n\n for (const keyValueString of splittedProps) {\n const keyValuePair = keyValueString.split(\"=\").map((el) => el.trim());\n if (keyValuePair.length !== 2 || keyValuePair[0] === \"\" || keyValuePair[1] === \"\") continue;\n // @ts-ignore\n propsObj[keyValuePair[0]] = keyValuePair[1];\n }\n\n return Object.keys(propsObj).length === 0 ? undefined : propsObj;\n};\n", "export const resolvePath = (pathOrProps?: string): string => {\n if (pathOrProps) return pathOrProps;\n\n const sources = [\n { value: document.body?.getAttribute(\"data-s-path\"), name: \"data-s-path\" },\n { value: document.body?.getAttribute(\"data-s:path\"), name: \"data-s:path\" }\n ];\n\n // Only keep sources that actually exist\n const existing = sources.filter(({ value }) => value);\n\n if (existing.length > 1) {\n console.warn(\"[onedollarstats] Multiple path sources found. Using priority order:\", existing.map(({ name }) => name).join(\" > \"));\n }\n\n // Return first available value, fallback to location.pathname\n return existing[0]?.value ?? location.pathname;\n};\n", "import type { AnalyticsConfig } from \"../types\";\n\nconst matchesPattern = (path: string, pattern: string): boolean => {\n // Escape special regex characters except '*' which becomes '.*'\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*/g, \".*\");\n return new RegExp(`^${escaped}$`).test(path);\n};\n\nexport const shouldTrackPath = (path: string, config: Required<AnalyticsConfig>): boolean => {\n // Exclude pages first\n if (config.excludePages.some((pattern) => matchesPattern(path, pattern))) return false;\n // If includePages is defined, only allow matching paths\n if (config.includePages.length && !config.includePages.some((pattern) => matchesPattern(path, pattern))) return false;\n return true;\n};\n", "import type { AnalyticsConfig, BaseProps, BodyToSend, Event, ViewArguments } from \"./types\";\nimport { createDebugModal } from \"./utils/create-modal\";\nimport { getEnvironment, isClient } from \"./utils/environment\";\nimport { parseUtmParams } from \"./utils/parse-utm-params\";\nimport { parseProps } from \"./utils/props-parser\";\nimport { resolvePath } from \"./utils/resolve-path\";\nimport { shouldTrackPath } from \"./utils/should-track\";\n\nexport const defaultConfig: Required<AnalyticsConfig> = {\n trackLocalhostAs: null,\n collectorUrl: \"https://collector.onedollarstats.com/events\",\n hashRouting: false,\n autocollect: true,\n excludePages: [],\n includePages: []\n};\n\nclass AnalyticsTracker {\n private static instance: AnalyticsTracker | null = null;\n\n private autocollectSetupDone = false;\n private config: Required<AnalyticsConfig>;\n private lastPage: string | null = null;\n private modalLog: (message: string, success: boolean) => void = () => {};\n\n public static getInstance(userConfig: AnalyticsConfig = {}): AnalyticsTracker {\n // Fresh no-op instance for SSR\n if (!isClient()) return new AnalyticsTracker(userConfig);\n\n if (!AnalyticsTracker.instance) {\n AnalyticsTracker.instance = new AnalyticsTracker(userConfig);\n }\n return AnalyticsTracker.instance;\n }\n\n private constructor(userConfig: AnalyticsConfig = {}) {\n this.config = { ...defaultConfig, ...userConfig };\n\n // Skip setup in non-client environments\n if (!isClient()) return;\n\n const { isLocalhost } = getEnvironment();\n\n // Log connection only if on localhost and tracking is configured\n if (isLocalhost && this.config.trackLocalhostAs) {\n console.log(`[onedollarstats]\\nOneDollarStats successfully connected! Tracking your localhost as ${this.config.trackLocalhostAs}`);\n this.modalLog = createDebugModal(this.config.trackLocalhostAs, this.config.collectorUrl);\n }\n\n // Auto-start autocollect\n if (this.config.autocollect) this.setupAutocollect();\n }\n\n private async sendWithBeaconOrFetch(stringifiedBody: string, callback: (success: boolean) => void): Promise<void> {\n // First fallback: try sendBeacon\n if (navigator.sendBeacon?.(this.config.collectorUrl, stringifiedBody)) {\n callback(true);\n return;\n }\n\n // Second fallback: use fetch() with keepalive\n fetch(this.config.collectorUrl, {\n method: \"POST\",\n body: stringifiedBody,\n headers: { \"Content-Type\": \"application/json\" },\n keepalive: true\n })\n .then(({ ok }) => callback(ok))\n .catch((err: Error) => {\n console.error(\"[onedollarstats] fetch() failed:\", err.message);\n callback(false);\n });\n }\n\n // Handles localhost replacement, referrer, UTM parameters, and debug mode.\n // Uses img beacon then `navigator.sendBeacon` if available, otherwise falls back to `fetch`.\n private async send(data: Event): Promise<void> {\n const { isLocalhost, isHeadlessBrowser } = getEnvironment();\n if ((isLocalhost && !this.config.trackLocalhostAs) || isHeadlessBrowser) return;\n\n let urlToSend: URL = new URL(location.href);\n\n // Determine debug mode and handle localhost replacement\n let isDebug: boolean = false;\n if (isLocalhost && this.config.trackLocalhostAs) {\n try {\n const debugUrl = new URL(`https://${this.config.trackLocalhostAs}${urlToSend.pathname}`);\n if (urlToSend.hostname !== debugUrl.hostname) {\n isDebug = true;\n urlToSend = debugUrl;\n }\n } catch {\n return;\n }\n }\n\n // Clean query string unless UTM is explicitly provided\n urlToSend.search = \"\";\n if (data.path) urlToSend.pathname = data.path;\n\n const cleanUrl = urlToSend.href.replace(/\\/$/, \"\");\n\n // Determine referrer\n let referrer: string | undefined = data.referrer;\n try {\n if (!referrer && document.referrer && document.referrer !== \"null\") {\n const referrerURL = new URL(document.referrer);\n if (referrerURL.hostname !== urlToSend.hostname) referrer = referrerURL.href;\n }\n } catch {} // ignore malformed referrer\n\n // Build request body\n const body: BodyToSend = {\n u: cleanUrl,\n e: [\n {\n t: data.type,\n h: this.config.hashRouting,\n r: referrer,\n p: data.props\n }\n ]\n };\n\n if (data.utm && Object.keys(data.utm).length > 0) body.qs = data.utm;\n if (isDebug) {\n body.debug = true;\n let logMessage = `[onedollarstats]\\nEvent name: ${data.type}\\nEvent collected from: ${cleanUrl}`;\n if (data.props && Object.keys(data.props).length > 0) logMessage += `\\nProps: ${JSON.stringify(data.props, null, 2)}`;\n if (referrer) logMessage += `\\nReferrer: ${referrer}`;\n if (this.config.hashRouting) logMessage += `\\nHashRouting: ${this.config.hashRouting}`;\n if (data.utm && Object.keys(data.utm).length > 0) logMessage += `\\nUTM: ${data.utm}`;\n\n console.log(logMessage);\n }\n\n // Prepare the event payload\n const stringifiedBody = JSON.stringify(body);\n // Encode for safe inclusion in query string using Base64\n const payloadBase64 = btoa(stringifiedBody);\n\n const safeGetThreshold = 1500; // limit for query-string-containing URLs\n const tryImageBeacon = payloadBase64.length <= safeGetThreshold;\n\n const onComplete = (success: boolean) => this.modalLog(`${data.type} ${success ? \"sent\" : \"failed to send\"}`, success);\n\n if (tryImageBeacon) {\n // Send via image beacon\n const img = new Image(1, 1);\n\n img.onload = () => onComplete(true);\n // If loading image fails (server unavailable, blocked, etc.)\n img.onerror = () => this.sendWithBeaconOrFetch(stringifiedBody, onComplete);\n\n // Primary attempt: send data via image beacon (GET request with query string)\n img.src = `${this.config.collectorUrl}?data=${payloadBase64}`;\n } else await this.sendWithBeaconOrFetch(stringifiedBody, onComplete);\n }\n\n // Prevents duplicate pageviews and respects include/exclude page rules. Automatically parses UTM parameters from URL.\n private trackPageView({ path, props }: ViewArguments, checkBlock: boolean = false) {\n if (!isClient()) return;\n\n const viewPath = resolvePath(path);\n\n const viewProps =\n props ||\n (() => {\n const newProps = {};\n const elements = document.querySelectorAll(\"[data-s\\\\:view-props], [data-s-view-props]\");\n\n for (const el of Array.from(elements)) {\n const propsString = el.getAttribute(\"data-s-view-props\") || el.getAttribute(\"data-s:view-props\");\n if (!propsString) continue;\n const parsedProps = parseProps(propsString);\n Object.assign(newProps, parsedProps);\n }\n\n return Object.keys(newProps).length ? newProps : undefined;\n })();\n\n // Skip duplicate pageviews or excluded pages\n if (!this.config.hashRouting && this.lastPage === viewPath) return;\n\n // Skip page if checkBlock is true and the path should be excluded\n if (checkBlock && !shouldTrackPath(viewPath, this.config)) return;\n\n this.lastPage = viewPath;\n\n const utm = parseUtmParams(new URLSearchParams(location.search));\n this.send({ type: \"PageView\", path: viewPath, props: viewProps, utm });\n }\n\n /**\n * Tracks a custom event.\n * Can accept path string or a props object.\n *\n * @param eventName Name of the event to track.\n * @param pathOrProps Optional path string or props object.\n * @param props Optional props object if path string is provided.\n */\n public async event(eventName: string, pathOrProps?: string | BaseProps, props?: BaseProps) {\n if (!isClient()) return;\n\n const { isLocalhost, isHeadlessBrowser } = getEnvironment();\n if ((isLocalhost && !this.config.trackLocalhostAs) || isHeadlessBrowser) return;\n\n const args: ViewArguments = {};\n if (typeof pathOrProps === \"string\") {\n args.path = resolvePath(pathOrProps);\n\n args.props = props;\n } else if (typeof pathOrProps === \"object\") args.props = pathOrProps;\n\n this.send({ type: eventName, ...args });\n }\n\n /**\n * Records a page view.\n * Can accept path string or a props object.\n *\n * @param pathOrProps Optional path string or props object.\n * @param props Optional props when first arg is a path string.\n */\n public async view(pathOrProps?: string | BaseProps, props?: BaseProps) {\n if (!isClient()) return;\n\n const args: ViewArguments = {};\n\n if (typeof pathOrProps === \"string\") {\n args.path = pathOrProps;\n args.props = props;\n } else if (typeof pathOrProps === \"object\") {\n args.props = pathOrProps;\n }\n\n this.trackPageView(args);\n }\n\n /**\n * Installs global DOM/window listeners exactly once for:\n * - visibilitychange\n * - history.pushState\n * - popstate\n * - hashchange\n * - click autocapture for elements annotated with `data-s:event` & `data-s-event`\n *\n */\n private setupAutocollect() {\n if (!isClient() || this.autocollectSetupDone) return;\n this.autocollectSetupDone = true;\n\n const handlePageView = () => this.trackPageView({}, true);\n\n // visibilitychange\n const onVisibility = () => {\n if (document.visibilityState === \"visible\") handlePageView();\n };\n document.addEventListener(\"visibilitychange\", onVisibility);\n\n // pushState\n const origPush = history.pushState.bind(history);\n history.pushState = (...args) => {\n origPush(...args);\n requestAnimationFrame(() => {\n handlePageView();\n });\n };\n\n // popstate\n window.addEventListener(\"popstate\", handlePageView);\n\n // hashchange\n window.addEventListener(\"hashchange\", handlePageView);\n\n // click autocapture\n const onClick: EventListener = (ev: Event) => {\n const clickEvent = ev as MouseEvent;\n if (clickEvent.type === \"auxclick\" && clickEvent.button !== 1) return;\n\n const target = clickEvent.target as Element | null;\n if (!target) return;\n\n // Check if inside <a> or <button>\n const insideInteractive = !!target.closest(\"a, button\");\n\n let el: Element | null = target;\n let depth = 0;\n\n while (el) {\n const eventName = el.getAttribute(\"data-s-event\") || el.getAttribute(\"data-s:event\");\n if (eventName) {\n const propsAttr = el.getAttribute(\"data-s-event-props\") || el.getAttribute(\"data-s:event-props\");\n const props = propsAttr ? parseProps(propsAttr) : undefined;\n const path = el.getAttribute(\"data-s-event-path\") || el.getAttribute(\"data-s:event-path\") || undefined;\n\n if ((path && !shouldTrackPath(path, this.config)) || !shouldTrackPath(location.pathname, this.config)) {\n return;\n }\n\n this.event(eventName, path ?? props, props);\n return;\n }\n\n el = el.parentElement;\n depth++;\n\n // If not in <a>/<button>, stop after 3 levels\n if (!insideInteractive && depth >= 3) break;\n }\n };\n\n document.addEventListener(\"click\", onClick);\n\n // Fire initial pageview if already visible\n if (document.visibilityState === \"visible\") handlePageView();\n }\n}\n\nexport const configure = (userConfig: AnalyticsConfig = {}) => {\n AnalyticsTracker.getInstance(userConfig);\n};\n\nexport const event = async (eventName: string, pathOrProps?: string | BaseProps, props?: BaseProps) => {\n const instance = AnalyticsTracker.getInstance();\n await instance.event(eventName, pathOrProps, props);\n};\n\nexport const view = async (pathOrProps?: string | BaseProps, props?: BaseProps) => {\n const instance = AnalyticsTracker.getInstance();\n await instance.view(pathOrProps, props);\n};\n"],
|
|
5
|
+
"mappings": ";AAEO,IAAM,mBAAmB,CAAC,UAAkB,iBAAyB;AAC1E,MAAI,CAAC,SAAS,eAAe,6BAA6B,GAAG;AAC3D,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,KAAK;AACX,UAAM,cAAc;AACpB,aAAS,KAAK,YAAY,KAAK;AAAA,EACjC;AAGA,QAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,QAAM,YAAY;AAClB,QAAM,YAAY;AAAA;AAAA;AAAA,SAGX,MAAM,IAAI,4CAA4C,QAAQ;AAAA;AAAA;AAGrE,WAAS,KAAK,YAAY,KAAK;AAG/B,QAAM,cAAc,YAAY,GAAG,iBAAiB,SAAS,MAAM,MAAM,OAAO,GAAG,EAAE,MAAM,KAAK,CAAC;AAGjG,MAAI,iBAAiB,cAAc,cAAc;AAC/C,UAAM,MAAM,IAAI,MAAM,GAAG,CAAC;AAC1B,QAAI,UAAU,MAAM;AAClB,UAAI,MAAM,cAAc,qBAAqB,EAAG;AAChD,YAAM,UAAU,SAAS,cAAc,GAAG;AAC1C,cAAQ,KAAK;AACb,cAAQ,YAAY,GAAG,MAAM,OAAO;AACpC,YAAM,cAAc,QAAQ,GAAG,sBAAsB,YAAY,OAAO;AAAA,IAC1E;AACA,QAAI,MAAM;AAAA,EACZ;AAGA,SAAO,CAAC,SAAiB,YAA2B;AAClD,UAAM,eAAe,MAAM,cAAc,YAAY;AACrD,QAAI,CAAC,gBAAgB,MAAM,cAAc,qBAAqB,EAAG;AAEjE,UAAM,QAAQ,SAAS,cAAc,GAAG;AACxC,UAAM,YAAY,GAAG,UAAU,MAAM,UAAU,MAAM,KAAK,sBAAsB,OAAO;AACvF,iBAAa,YAAY,KAAK;AAC9B,iBAAa,YAAY,aAAa;AAAA,EACxC;AACF;AAEA,IAAM,QAAQ;AAAA,EACZ,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AACX;AAEA,IAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACxDL,IAAM,iBAAiB,OAGxB;AAAA,EACJ,aAAa,sDAAsD,KAAK,SAAS,QAAQ,KAAK,SAAS,aAAa;AAAA,EACpH,mBAAmB;AAAA,IACjB,OAAO,UAAU,aACd,cAAc,UAAU,OAAO,YAC/B,iBAAiB,UAAU,OAAO,eAClC,aAAa,UAAU,OAAO;AAAA,EACnC;AACF;AACO,IAAM,WAAW,MAAe;AACrC,MAAI;AAEF,QAAI,OAAO,WAAW,eAAe,OAAO,aAAa,YAAa,QAAO;AAG7E,UAAM,KAAK,OAAO,cAAc,cAAc,UAAU,YAAY;AACpE,QAAI,cAAc,KAAK,EAAE,EAAG,QAAO;AACnC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACxBO,IAAM,iBAAiB,CAAC,oBAAqC;AAClE,QAAM,MAAyC,CAAC;AAEhD,GAAC,gBAAgB,cAAc,cAAc,YAAY,aAAa,EAAE,QAAQ,CAAC,QAAQ;AACvF,UAAM,SAAS,gBAAgB,OAAO,GAAG;AACzC,QAAI,OAAO,WAAW,GAAG;AACvB,UAAI,GAAG,IAAI,OAAO,CAAC;AAAA,IACrB,WAAW,OAAO,SAAS,GAAG;AAC5B,UAAI,GAAG,IAAI;AAAA,IACb;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;ACbO,IAAM,aAAa,CAAC,gBAA4D;AACrF,MAAI,CAAC,YAAa,QAAO;AAGzB,QAAM,gBAAgB,YAAY,MAAM,GAAG;AAC3C,QAAM,WAAmC,CAAC;AAE1C,aAAW,kBAAkB,eAAe;AAC1C,UAAM,eAAe,eAAe,MAAM,GAAG,EAAE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;AACpE,QAAI,aAAa,WAAW,KAAK,aAAa,CAAC,MAAM,MAAM,aAAa,CAAC,MAAM,GAAI;AAEnF,aAAS,aAAa,CAAC,CAAC,IAAI,aAAa,CAAC;AAAA,EAC5C;AAEA,SAAO,OAAO,KAAK,QAAQ,EAAE,WAAW,IAAI,SAAY;AAC1D;;;ACfO,IAAM,cAAc,CAAC,gBAAiC;AAC3D,MAAI,YAAa,QAAO;AAExB,QAAM,UAAU;AAAA,IACd,EAAE,OAAO,SAAS,MAAM,aAAa,aAAa,GAAG,MAAM,cAAc;AAAA,IACzE,EAAE,OAAO,SAAS,MAAM,aAAa,aAAa,GAAG,MAAM,cAAc;AAAA,EAC3E;AAGA,QAAM,WAAW,QAAQ,OAAO,CAAC,EAAE,MAAM,MAAM,KAAK;AAEpD,MAAI,SAAS,SAAS,GAAG;AACvB,YAAQ,KAAK,uEAAuE,SAAS,IAAI,CAAC,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,KAAK,CAAC;AAAA,EAClI;AAGA,SAAO,SAAS,CAAC,GAAG,SAAS,SAAS;AACxC;;;ACfA,IAAM,iBAAiB,CAAC,MAAc,YAA6B;AAEjE,QAAM,UAAU,QAAQ,QAAQ,qBAAqB,MAAM,EAAE,QAAQ,OAAO,IAAI;AAChF,SAAO,IAAI,OAAO,IAAI,OAAO,GAAG,EAAE,KAAK,IAAI;AAC7C;AAEO,IAAM,kBAAkB,CAAC,MAAc,WAA+C;AAE3F,MAAI,OAAO,aAAa,KAAK,CAAC,YAAY,eAAe,MAAM,OAAO,CAAC,EAAG,QAAO;AAEjF,MAAI,OAAO,aAAa,UAAU,CAAC,OAAO,aAAa,KAAK,CAAC,YAAY,eAAe,MAAM,OAAO,CAAC,EAAG,QAAO;AAChH,SAAO;AACT;;;ACNO,IAAM,gBAA2C;AAAA,EACtD,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AAAA,EACb,cAAc,CAAC;AAAA,EACf,cAAc,CAAC;AACjB;AAEA,IAAM,oBAAN,MAAM,kBAAiB;AAAA,EAkBb,YAAY,aAA8B,CAAC,GAAG;AAftD,SAAQ,uBAAuB;AAE/B,SAAQ,WAA0B;AAClC,SAAQ,WAAwD,MAAM;AAAA,IAAC;AAarE,SAAK,SAAS,EAAE,GAAG,eAAe,GAAG,WAAW;AAGhD,QAAI,CAAC,SAAS,EAAG;AAEjB,UAAM,EAAE,YAAY,IAAI,eAAe;AAGvC,QAAI,eAAe,KAAK,OAAO,kBAAkB;AAC/C,cAAQ,IAAI;AAAA,oEAAuF,KAAK,OAAO,gBAAgB,EAAE;AACjI,WAAK,WAAW,iBAAiB,KAAK,OAAO,kBAAkB,KAAK,OAAO,YAAY;AAAA,IACzF;AAGA,QAAI,KAAK,OAAO,YAAa,MAAK,iBAAiB;AAAA,EACrD;AAAA,EA1BA,OAAc,YAAY,aAA8B,CAAC,GAAqB;AAE5E,QAAI,CAAC,SAAS,EAAG,QAAO,IAAI,kBAAiB,UAAU;AAEvD,QAAI,CAAC,kBAAiB,UAAU;AAC9B,wBAAiB,WAAW,IAAI,kBAAiB,UAAU;AAAA,IAC7D;AACA,WAAO,kBAAiB;AAAA,EAC1B;AAAA,EAoBA,MAAc,sBAAsB,iBAAyB,UAAqD;AAEhH,QAAI,UAAU,aAAa,KAAK,OAAO,cAAc,eAAe,GAAG;AACrE,eAAS,IAAI;AACb;AAAA,IACF;AAGA,UAAM,KAAK,OAAO,cAAc;AAAA,MAC9B,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,WAAW;AAAA,IACb,CAAC,EACE,KAAK,CAAC,EAAE,GAAG,MAAM,SAAS,EAAE,CAAC,EAC7B,MAAM,CAAC,QAAe;AACrB,cAAQ,MAAM,oCAAoC,IAAI,OAAO;AAC7D,eAAS,KAAK;AAAA,IAChB,CAAC;AAAA,EACL;AAAA;AAAA;AAAA,EAIA,MAAc,KAAK,MAA4B;AAC7C,UAAM,EAAE,aAAa,kBAAkB,IAAI,eAAe;AAC1D,QAAK,eAAe,CAAC,KAAK,OAAO,oBAAqB,kBAAmB;AAEzE,QAAI,YAAiB,IAAI,IAAI,SAAS,IAAI;AAG1C,QAAI,UAAmB;AACvB,QAAI,eAAe,KAAK,OAAO,kBAAkB;AAC/C,UAAI;AACF,cAAM,WAAW,IAAI,IAAI,WAAW,KAAK,OAAO,gBAAgB,GAAG,UAAU,QAAQ,EAAE;AACvF,YAAI,UAAU,aAAa,SAAS,UAAU;AAC5C,oBAAU;AACV,sBAAY;AAAA,QACd;AAAA,MACF,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AAGA,cAAU,SAAS;AACnB,QAAI,KAAK,KAAM,WAAU,WAAW,KAAK;AAEzC,UAAM,WAAW,UAAU,KAAK,QAAQ,OAAO,EAAE;AAGjD,QAAI,WAA+B,KAAK;AACxC,QAAI;AACF,UAAI,CAAC,YAAY,SAAS,YAAY,SAAS,aAAa,QAAQ;AAClE,cAAM,cAAc,IAAI,IAAI,SAAS,QAAQ;AAC7C,YAAI,YAAY,aAAa,UAAU,SAAU,YAAW,YAAY;AAAA,MAC1E;AAAA,IACF,QAAQ;AAAA,IAAC;AAGT,UAAM,OAAmB;AAAA,MACvB,GAAG;AAAA,MACH,GAAG;AAAA,QACD;AAAA,UACE,GAAG,KAAK;AAAA,UACR,GAAG,KAAK,OAAO;AAAA,UACf,GAAG;AAAA,UACH,GAAG,KAAK;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAEA,QAAI,KAAK,OAAO,OAAO,KAAK,KAAK,GAAG,EAAE,SAAS,EAAG,MAAK,KAAK,KAAK;AACjE,QAAI,SAAS;AACX,WAAK,QAAQ;AACb,UAAI,aAAa;AAAA,cAAiC,KAAK,IAAI;AAAA,wBAA2B,QAAQ;AAC9F,UAAI,KAAK,SAAS,OAAO,KAAK,KAAK,KAAK,EAAE,SAAS,EAAG,eAAc;AAAA,SAAY,KAAK,UAAU,KAAK,OAAO,MAAM,CAAC,CAAC;AACnH,UAAI,SAAU,eAAc;AAAA,YAAe,QAAQ;AACnD,UAAI,KAAK,OAAO,YAAa,eAAc;AAAA,eAAkB,KAAK,OAAO,WAAW;AACpF,UAAI,KAAK,OAAO,OAAO,KAAK,KAAK,GAAG,EAAE,SAAS,EAAG,eAAc;AAAA,OAAU,KAAK,GAAG;AAElF,cAAQ,IAAI,UAAU;AAAA,IACxB;AAGA,UAAM,kBAAkB,KAAK,UAAU,IAAI;AAE3C,UAAM,gBAAgB,KAAK,eAAe;AAE1C,UAAM,mBAAmB;AACzB,UAAM,iBAAiB,cAAc,UAAU;AAE/C,UAAM,aAAa,CAAC,YAAqB,KAAK,SAAS,GAAG,KAAK,IAAI,IAAI,UAAU,SAAS,gBAAgB,IAAI,OAAO;AAErH,QAAI,gBAAgB;AAElB,YAAM,MAAM,IAAI,MAAM,GAAG,CAAC;AAE1B,UAAI,SAAS,MAAM,WAAW,IAAI;AAElC,UAAI,UAAU,MAAM,KAAK,sBAAsB,iBAAiB,UAAU;AAG1E,UAAI,MAAM,GAAG,KAAK,OAAO,YAAY,SAAS,aAAa;AAAA,IAC7D,MAAO,OAAM,KAAK,sBAAsB,iBAAiB,UAAU;AAAA,EACrE;AAAA;AAAA,EAGQ,cAAc,EAAE,MAAM,MAAM,GAAkB,aAAsB,OAAO;AACjF,QAAI,CAAC,SAAS,EAAG;AAEjB,UAAM,WAAW,YAAY,IAAI;AAEjC,UAAM,YACJ,UACC,MAAM;AACL,YAAM,WAAW,CAAC;AAClB,YAAM,WAAW,SAAS,iBAAiB,4CAA4C;AAEvF,iBAAW,MAAM,MAAM,KAAK,QAAQ,GAAG;AACrC,cAAM,cAAc,GAAG,aAAa,mBAAmB,KAAK,GAAG,aAAa,mBAAmB;AAC/F,YAAI,CAAC,YAAa;AAClB,cAAM,cAAc,WAAW,WAAW;AAC1C,eAAO,OAAO,UAAU,WAAW;AAAA,MACrC;AAEA,aAAO,OAAO,KAAK,QAAQ,EAAE,SAAS,WAAW;AAAA,IACnD,GAAG;AAGL,QAAI,CAAC,KAAK,OAAO,eAAe,KAAK,aAAa,SAAU;AAG5D,QAAI,cAAc,CAAC,gBAAgB,UAAU,KAAK,MAAM,EAAG;AAE3D,SAAK,WAAW;AAEhB,UAAM,MAAM,eAAe,IAAI,gBAAgB,SAAS,MAAM,CAAC;AAC/D,SAAK,KAAK,EAAE,MAAM,YAAY,MAAM,UAAU,OAAO,WAAW,IAAI,CAAC;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,MAAM,WAAmB,aAAkC,OAAmB;AACzF,QAAI,CAAC,SAAS,EAAG;AAEjB,UAAM,EAAE,aAAa,kBAAkB,IAAI,eAAe;AAC1D,QAAK,eAAe,CAAC,KAAK,OAAO,oBAAqB,kBAAmB;AAEzE,UAAM,OAAsB,CAAC;AAC7B,QAAI,OAAO,gBAAgB,UAAU;AACnC,WAAK,OAAO,YAAY,WAAW;AAEnC,WAAK,QAAQ;AAAA,IACf,WAAW,OAAO,gBAAgB,SAAU,MAAK,QAAQ;AAEzD,SAAK,KAAK,EAAE,MAAM,WAAW,GAAG,KAAK,CAAC;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,KAAK,aAAkC,OAAmB;AACrE,QAAI,CAAC,SAAS,EAAG;AAEjB,UAAM,OAAsB,CAAC;AAE7B,QAAI,OAAO,gBAAgB,UAAU;AACnC,WAAK,OAAO;AACZ,WAAK,QAAQ;AAAA,IACf,WAAW,OAAO,gBAAgB,UAAU;AAC1C,WAAK,QAAQ;AAAA,IACf;AAEA,SAAK,cAAc,IAAI;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,mBAAmB;AACzB,QAAI,CAAC,SAAS,KAAK,KAAK,qBAAsB;AAC9C,SAAK,uBAAuB;AAE5B,UAAM,iBAAiB,MAAM,KAAK,cAAc,CAAC,GAAG,IAAI;AAGxD,UAAM,eAAe,MAAM;AACzB,UAAI,SAAS,oBAAoB,UAAW,gBAAe;AAAA,IAC7D;AACA,aAAS,iBAAiB,oBAAoB,YAAY;AAG1D,UAAM,WAAW,QAAQ,UAAU,KAAK,OAAO;AAC/C,YAAQ,YAAY,IAAI,SAAS;AAC/B,eAAS,GAAG,IAAI;AAChB,4BAAsB,MAAM;AAC1B,uBAAe;AAAA,MACjB,CAAC;AAAA,IACH;AAGA,WAAO,iBAAiB,YAAY,cAAc;AAGlD,WAAO,iBAAiB,cAAc,cAAc;AAGpD,UAAM,UAAyB,CAAC,OAAc;AAC5C,YAAM,aAAa;AACnB,UAAI,WAAW,SAAS,cAAc,WAAW,WAAW,EAAG;AAE/D,YAAM,SAAS,WAAW;AAC1B,UAAI,CAAC,OAAQ;AAGb,YAAM,oBAAoB,CAAC,CAAC,OAAO,QAAQ,WAAW;AAEtD,UAAI,KAAqB;AACzB,UAAI,QAAQ;AAEZ,aAAO,IAAI;AACT,cAAM,YAAY,GAAG,aAAa,cAAc,KAAK,GAAG,aAAa,cAAc;AACnF,YAAI,WAAW;AACb,gBAAM,YAAY,GAAG,aAAa,oBAAoB,KAAK,GAAG,aAAa,oBAAoB;AAC/F,gBAAM,QAAQ,YAAY,WAAW,SAAS,IAAI;AAClD,gBAAM,OAAO,GAAG,aAAa,mBAAmB,KAAK,GAAG,aAAa,mBAAmB,KAAK;AAE7F,cAAK,QAAQ,CAAC,gBAAgB,MAAM,KAAK,MAAM,KAAM,CAAC,gBAAgB,SAAS,UAAU,KAAK,MAAM,GAAG;AACrG;AAAA,UACF;AAEA,eAAK,MAAM,WAAW,QAAQ,OAAO,KAAK;AAC1C;AAAA,QACF;AAEA,aAAK,GAAG;AACR;AAGA,YAAI,CAAC,qBAAqB,SAAS,EAAG;AAAA,MACxC;AAAA,IACF;AAEA,aAAS,iBAAiB,SAAS,OAAO;AAG1C,QAAI,SAAS,oBAAoB,UAAW,gBAAe;AAAA,EAC7D;AACF;AA5SM,kBACW,WAAoC;AADrD,IAAM,mBAAN;AA8SO,IAAM,YAAY,CAAC,aAA8B,CAAC,MAAM;AAC7D,mBAAiB,YAAY,UAAU;AACzC;AAEO,IAAM,QAAQ,OAAO,WAAmB,aAAkC,UAAsB;AACrG,QAAM,WAAW,iBAAiB,YAAY;AAC9C,QAAM,SAAS,MAAM,WAAW,aAAa,KAAK;AACpD;AAEO,IAAM,OAAO,OAAO,aAAkC,UAAsB;AACjF,QAAM,WAAW,iBAAiB,YAAY;AAC9C,QAAM,SAAS,KAAK,aAAa,KAAK;AACxC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const createDebugModal: (debugUrl: string, analyticsUrl: string) => (message: string, success: boolean) => void;
|