@faststats/web 0.13.0 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{extensions-BfubKIzk.d.ts → extensions-C2k9Im6I.d.ts} +0 -2
- package/dist/chunks/identifiers-BHYhM7DM.js +15 -0
- package/dist/chunks/send-data-BCVUgwry.js +174 -0
- package/dist/chunks/session-manager-DBWYTyfd.js +114 -0
- package/dist/error.d.ts +4 -4
- package/dist/error.js +48 -61
- package/dist/flags.d.ts +2 -4
- package/dist/flags.js +3 -4
- package/dist/index.d.ts +2 -9
- package/dist/index.js +54 -127
- package/dist/replay.d.ts +38 -71
- package/dist/replay.js +424 -444
- package/dist/web-vitals.d.ts +2 -4
- package/dist/web-vitals.js +5 -22
- package/package.json +3 -3
- package/dist/chunks/identifiers-DOtve3E5.js +0 -18
- package/dist/chunks/send-data-C76HbZ8d.js +0 -96
- package/dist/chunks/session-manager-DUpF3mi3.js +0 -149
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/extensions.d.ts
|
|
2
1
|
type ExtensionHooks = {
|
|
3
2
|
pageChange: {
|
|
4
3
|
url: string;
|
|
@@ -40,5 +39,4 @@ interface AnalyticsExtension {
|
|
|
40
39
|
type ExtensionCleanup = (options: {
|
|
41
40
|
discard: boolean;
|
|
42
41
|
}) => void;
|
|
43
|
-
//#endregion
|
|
44
42
|
export { ExtensionHook as a, ExtensionHandler as i, AnalyticsExtensionContext as n, ExtensionHooks as o, ExtensionCleanup as r, AnalyticsExtension as t };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { o as getStorageItem, s as setStorageItem, t as createId } from "./session-manager-DBWYTyfd.js";
|
|
2
|
+
// src/utils/identifiers.ts
|
|
3
|
+
const key = (siteKey) => `faststats_anon_id_${siteKey}`;
|
|
4
|
+
/** The visitor's persisted anonymous ID, or "" when it cannot be stored. */
|
|
5
|
+
function getAnonymousId(siteKey, cookieless = false) {
|
|
6
|
+
if (cookieless) return "";
|
|
7
|
+
const existing = getStorageItem("localStorage", key(siteKey));
|
|
8
|
+
if (existing) return existing;
|
|
9
|
+
const id = createId();
|
|
10
|
+
return setStorageItem("localStorage", key(siteKey), id) ? id : "";
|
|
11
|
+
}
|
|
12
|
+
function resetAnonymousId(siteKey, cookieless = false) {
|
|
13
|
+
return !cookieless && setStorageItem("localStorage", key(siteKey), null) ? getAnonymousId(siteKey) : "";
|
|
14
|
+
}
|
|
15
|
+
export { resetAnonymousId as n, getAnonymousId as t };
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// src/extensions.ts
|
|
2
|
+
/** Runs a built-in tracker with the client's settings and page lifecycle. */
|
|
3
|
+
function trackerExtension(name, create, hook) {
|
|
4
|
+
return {
|
|
5
|
+
name,
|
|
6
|
+
setup(context) {
|
|
7
|
+
const { siteKey, baseUrl, debug, trackHash, sdkName, sdkVersion } = context;
|
|
8
|
+
const tracker = create({
|
|
9
|
+
siteKey,
|
|
10
|
+
baseUrl,
|
|
11
|
+
debug,
|
|
12
|
+
trackHash,
|
|
13
|
+
cookieless: context.isCookieless(),
|
|
14
|
+
sdkName,
|
|
15
|
+
sdkVersion
|
|
16
|
+
});
|
|
17
|
+
context.on("pageHide", ({ persisted, terminal }) => tracker.onPageHidden(persisted, terminal));
|
|
18
|
+
context.on("consentChange", ({ cookieless }) => tracker.setCookielessMode(cookieless));
|
|
19
|
+
hook?.(tracker, context);
|
|
20
|
+
tracker.start();
|
|
21
|
+
return ({ discard }) => tracker.stop(discard);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
var ExtensionHost = class {
|
|
26
|
+
context;
|
|
27
|
+
log;
|
|
28
|
+
handlers = /* @__PURE__ */ new Map();
|
|
29
|
+
cleanups = [];
|
|
30
|
+
stopped = false;
|
|
31
|
+
constructor(extensions, context, log) {
|
|
32
|
+
this.context = context;
|
|
33
|
+
this.log = log;
|
|
34
|
+
for (const extension of extensions) this.setup(extension);
|
|
35
|
+
}
|
|
36
|
+
emit(hook, event) {
|
|
37
|
+
if (this.stopped) return;
|
|
38
|
+
for (const handler of this.handlers.get(hook) ?? []) try {
|
|
39
|
+
handler(event)?.catch((error) => this.log(`${hook} failed`, error));
|
|
40
|
+
} catch (error) {
|
|
41
|
+
this.log(`${hook} failed`, error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
stop(discard = false) {
|
|
45
|
+
if (this.stopped) return;
|
|
46
|
+
this.stopped = true;
|
|
47
|
+
for (const cleanup of this.cleanups.reverse()) try {
|
|
48
|
+
cleanup({ discard });
|
|
49
|
+
} catch (error) {
|
|
50
|
+
this.log("cleanup failed", error);
|
|
51
|
+
}
|
|
52
|
+
this.cleanups.length = 0;
|
|
53
|
+
this.handlers.clear();
|
|
54
|
+
}
|
|
55
|
+
async setup(extension) {
|
|
56
|
+
if (this.stopped) return;
|
|
57
|
+
const subscriptions = [];
|
|
58
|
+
try {
|
|
59
|
+
const cleanup = await extension.setup({
|
|
60
|
+
...this.context,
|
|
61
|
+
on: (hook, handler) => {
|
|
62
|
+
if (this.stopped) return () => {};
|
|
63
|
+
const handlers = this.handlers.get(hook) ?? /* @__PURE__ */ new Set();
|
|
64
|
+
this.handlers.set(hook, handlers);
|
|
65
|
+
handlers.add(handler);
|
|
66
|
+
const off = () => handlers.delete(handler);
|
|
67
|
+
subscriptions.push(off);
|
|
68
|
+
return off;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
if (this.stopped) cleanup?.({ discard: true });
|
|
72
|
+
else if (cleanup) this.cleanups.push(cleanup);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
for (const off of subscriptions) off();
|
|
75
|
+
this.log(`${extension.name} setup failed`, error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
// src/utils/api-urls.ts
|
|
80
|
+
function resolveBaseUrl(baseUrl) {
|
|
81
|
+
return baseUrl?.replace(/\/+$/, "") || "https://metrics.faststats.dev";
|
|
82
|
+
}
|
|
83
|
+
const URLS = {
|
|
84
|
+
events: "/v1/web",
|
|
85
|
+
identify: "/v1/identify",
|
|
86
|
+
replay: "/v1/replay",
|
|
87
|
+
vitals: "/v1/vitals"
|
|
88
|
+
};
|
|
89
|
+
function sanitizeUrl(url, includeHash = false) {
|
|
90
|
+
try {
|
|
91
|
+
const parsed = new URL(url, location.href);
|
|
92
|
+
return `${parsed.origin}${parsed.pathname}${includeHash ? parsed.hash : ""}`;
|
|
93
|
+
} catch {
|
|
94
|
+
return url.split(includeHash ? "?" : /[?#]/, 1)[0] ?? "";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const UTM_KEYS = [
|
|
98
|
+
"utm_source",
|
|
99
|
+
"utm_medium",
|
|
100
|
+
"utm_campaign",
|
|
101
|
+
"utm_term",
|
|
102
|
+
"utm_content"
|
|
103
|
+
];
|
|
104
|
+
function getReferrer() {
|
|
105
|
+
const raw = document.referrer;
|
|
106
|
+
if (!raw) return null;
|
|
107
|
+
try {
|
|
108
|
+
if (new URL(raw, location.href).origin === location.origin) return null;
|
|
109
|
+
} catch {}
|
|
110
|
+
return sanitizeUrl(raw);
|
|
111
|
+
}
|
|
112
|
+
/** Visitor environment sent with every event; unavailable values are omitted. */
|
|
113
|
+
function getClientContext() {
|
|
114
|
+
let timezone;
|
|
115
|
+
try {
|
|
116
|
+
timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
117
|
+
} catch {}
|
|
118
|
+
const { width, height } = globalThis.screen ?? {};
|
|
119
|
+
return {
|
|
120
|
+
timezone,
|
|
121
|
+
language: globalThis.navigator?.language,
|
|
122
|
+
screen: width && height ? `${width}x${height}` : void 0
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function getPageContext(includeHash = false) {
|
|
126
|
+
const hash = includeHash ? location.hash : "";
|
|
127
|
+
const ctx = {
|
|
128
|
+
page: `${location.pathname}${hash}`,
|
|
129
|
+
url: sanitizeUrl(location.href, includeHash),
|
|
130
|
+
referrer: getReferrer(),
|
|
131
|
+
title: document.title
|
|
132
|
+
};
|
|
133
|
+
if (!location.search) return ctx;
|
|
134
|
+
const sp = new URLSearchParams(location.search);
|
|
135
|
+
for (const key of UTM_KEYS) {
|
|
136
|
+
const value = sp.get(key);
|
|
137
|
+
if (value) ctx[key] = value;
|
|
138
|
+
}
|
|
139
|
+
return ctx;
|
|
140
|
+
}
|
|
141
|
+
// src/utils/send-data.ts
|
|
142
|
+
async function sendData(options) {
|
|
143
|
+
const { url, data, contentType = "application/json", debug = false, debugPrefix = "[Analytics]", useBeacon = true, keepalive = true, settleRejected } = options;
|
|
144
|
+
const log = (...details) => {
|
|
145
|
+
if (debug) console.log(debugPrefix, ...details);
|
|
146
|
+
};
|
|
147
|
+
if (useBeacon) try {
|
|
148
|
+
const body = data instanceof Blob ? data : new Blob([data], { type: contentType });
|
|
149
|
+
if (navigator.sendBeacon(url, body)) {
|
|
150
|
+
log("Sent via beacon");
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
} catch {}
|
|
154
|
+
const post = (keepalive) => fetch(url, {
|
|
155
|
+
method: "POST",
|
|
156
|
+
body: data,
|
|
157
|
+
headers: { "Content-Type": contentType },
|
|
158
|
+
keepalive,
|
|
159
|
+
credentials: "omit"
|
|
160
|
+
});
|
|
161
|
+
try {
|
|
162
|
+
const useKeepalive = keepalive && new Blob([data]).size <= 61440;
|
|
163
|
+
const { ok, status } = await post(useKeepalive).catch((error) => {
|
|
164
|
+
if (!useKeepalive) throw error;
|
|
165
|
+
return post(false);
|
|
166
|
+
});
|
|
167
|
+
log(ok ? "Sent via fetch" : status);
|
|
168
|
+
return ok || !!settleRejected && status < 500 && status !== 429;
|
|
169
|
+
} catch {
|
|
170
|
+
log("Failed to send");
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export { resolveBaseUrl as a, trackerExtension as c, getPageContext as i, URLS as n, sanitizeUrl as o, getClientContext as r, ExtensionHost as s, sendData as t };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// src/utils/storage.ts
|
|
2
|
+
function getStorageItem(name, key) {
|
|
3
|
+
try {
|
|
4
|
+
return globalThis[name]?.getItem(key) ?? null;
|
|
5
|
+
} catch {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/** Stores `value`, or removes the item for null. False when storage fails. */
|
|
10
|
+
function setStorageItem(name, key, value) {
|
|
11
|
+
try {
|
|
12
|
+
const storage = globalThis[name];
|
|
13
|
+
if (!storage) return false;
|
|
14
|
+
if (value === null) storage.removeItem(key);
|
|
15
|
+
else storage.setItem(key, value);
|
|
16
|
+
return true;
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// src/utils/session-manager.ts
|
|
22
|
+
const IDLE_MS = 18e5;
|
|
23
|
+
const MAX_AGE_MS = 864e5;
|
|
24
|
+
const SESSION_KEY = "faststats_session_";
|
|
25
|
+
const WINDOW_KEY = "faststats_window_id_";
|
|
26
|
+
const memory = /* @__PURE__ */ new Map();
|
|
27
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
28
|
+
const NO_STORAGE = {};
|
|
29
|
+
const slot = (siteKey, cookieless) => `${siteKey}:${cookieless ? "memory" : "persistent"}`;
|
|
30
|
+
function storageScope() {
|
|
31
|
+
try {
|
|
32
|
+
return globalThis.localStorage ?? NO_STORAGE;
|
|
33
|
+
} catch {
|
|
34
|
+
return NO_STORAGE;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function createId() {
|
|
38
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
|
|
39
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
40
|
+
}
|
|
41
|
+
function load(siteKey, cookieless) {
|
|
42
|
+
if (!cookieless) try {
|
|
43
|
+
const session = JSON.parse(getStorageItem("localStorage", `${SESSION_KEY}${siteKey}`) ?? "null");
|
|
44
|
+
if (typeof session?.id === "string" && session.id && Number.isFinite(session.start) && Number.isFinite(session.activity)) return session;
|
|
45
|
+
} catch {}
|
|
46
|
+
const entry = memory.get(slot(siteKey, cookieless));
|
|
47
|
+
return entry?.scope === storageScope() ? entry.session : null;
|
|
48
|
+
}
|
|
49
|
+
function save(siteKey, cookieless, session) {
|
|
50
|
+
const json = JSON.stringify(session);
|
|
51
|
+
if (cookieless || !setStorageItem("localStorage", SESSION_KEY + siteKey, json)) memory.set(slot(siteKey, cookieless), {
|
|
52
|
+
scope: storageScope(),
|
|
53
|
+
session
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function toContext(siteKey, cookieless, { id, start }) {
|
|
57
|
+
let windowId = cookieless ? id : getStorageItem("sessionStorage", WINDOW_KEY + siteKey);
|
|
58
|
+
if (!windowId) {
|
|
59
|
+
windowId = createId();
|
|
60
|
+
if (!setStorageItem("sessionStorage", WINDOW_KEY + siteKey, windowId)) windowId = id;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
sessionId: id,
|
|
64
|
+
windowId,
|
|
65
|
+
sessionStart: start
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function emit(siteKey, cookieless, prev, next) {
|
|
69
|
+
for (const listener of listeners.get(slot(siteKey, cookieless)) ?? []) listener(prev, next);
|
|
70
|
+
}
|
|
71
|
+
function resolveSession(siteKey, cookieless, touch) {
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
const existing = load(siteKey, cookieless);
|
|
74
|
+
const expired = !!existing && (now - existing.activity >= IDLE_MS || now - existing.start >= MAX_AGE_MS);
|
|
75
|
+
const session = existing && !expired ? existing : {
|
|
76
|
+
id: createId(),
|
|
77
|
+
start: now,
|
|
78
|
+
activity: now
|
|
79
|
+
};
|
|
80
|
+
if (touch) session.activity = now;
|
|
81
|
+
if (touch || session !== existing) save(siteKey, cookieless, session);
|
|
82
|
+
const context = toContext(siteKey, cookieless, session);
|
|
83
|
+
if (existing && expired) emit(siteKey, cookieless, toContext(siteKey, cookieless, existing), context);
|
|
84
|
+
return context;
|
|
85
|
+
}
|
|
86
|
+
/** The current session, replacing an expired one without counting activity. */
|
|
87
|
+
function getSessionContext(siteKey, cookieless = false) {
|
|
88
|
+
return resolveSession(siteKey, cookieless, false);
|
|
89
|
+
}
|
|
90
|
+
/** Counts visitor activity in the current session, which it returns. */
|
|
91
|
+
function touchActivity(siteKey, cookieless = false) {
|
|
92
|
+
return resolveSession(siteKey, cookieless, true);
|
|
93
|
+
}
|
|
94
|
+
function resetSession(siteKey, cookieless = false) {
|
|
95
|
+
const prev = getSessionContext(siteKey, cookieless);
|
|
96
|
+
memory.delete(slot(siteKey, cookieless));
|
|
97
|
+
if (!cookieless) {
|
|
98
|
+
setStorageItem("localStorage", SESSION_KEY + siteKey, null);
|
|
99
|
+
setStorageItem("sessionStorage", WINDOW_KEY + siteKey, null);
|
|
100
|
+
}
|
|
101
|
+
const next = getSessionContext(siteKey, cookieless);
|
|
102
|
+
if (prev.sessionId !== next.sessionId) emit(siteKey, cookieless, prev, next);
|
|
103
|
+
return next;
|
|
104
|
+
}
|
|
105
|
+
function onSessionRotated(siteKey, cookieless, listener) {
|
|
106
|
+
const key = slot(siteKey, cookieless);
|
|
107
|
+
const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
|
|
108
|
+
listeners.set(key, set.add(listener));
|
|
109
|
+
return () => {
|
|
110
|
+
set.delete(listener);
|
|
111
|
+
if (!set.size) listeners.delete(key);
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export { touchActivity as a, resetSession as i, getSessionContext as n, getStorageItem as o, onSessionRotated as r, setStorageItem as s, createId as t };
|
package/dist/error.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { t as AnalyticsExtension } from "./chunks/extensions-
|
|
2
|
-
//#region src/error.d.ts
|
|
1
|
+
import { t as AnalyticsExtension } from "./chunks/extensions-C2k9Im6I.js";
|
|
3
2
|
export type ErrorEntry = {
|
|
4
3
|
error: string;
|
|
5
4
|
message?: string;
|
|
@@ -39,6 +38,7 @@ export default class ErrorTracker {
|
|
|
39
38
|
private onError;
|
|
40
39
|
private onRejection;
|
|
41
40
|
private enqueue;
|
|
41
|
+
/** Returns true when the error was already queued. */
|
|
42
|
+
private add;
|
|
42
43
|
private flush;
|
|
43
|
-
}
|
|
44
|
-
//#endregion
|
|
44
|
+
}
|
package/dist/error.js
CHANGED
|
@@ -1,30 +1,14 @@
|
|
|
1
|
-
import { a as resolveBaseUrl, i as getPageContext, n as URLS, t as sendData } from "./chunks/send-data-
|
|
2
|
-
import { n as getSessionContext } from "./chunks/session-manager-
|
|
3
|
-
import { t as getAnonymousId } from "./chunks/identifiers-
|
|
4
|
-
|
|
1
|
+
import { a as resolveBaseUrl, c as trackerExtension, i as getPageContext, n as URLS, t as sendData } from "./chunks/send-data-BCVUgwry.js";
|
|
2
|
+
import { n as getSessionContext } from "./chunks/session-manager-DBWYTyfd.js";
|
|
3
|
+
import { t as getAnonymousId } from "./chunks/identifiers-BHYhM7DM.js";
|
|
4
|
+
// src/error.ts
|
|
5
5
|
const SDK_NAME = "@faststats/web";
|
|
6
|
-
const SDK_VERSION = "0.13.
|
|
6
|
+
const SDK_VERSION = "0.13.2";
|
|
7
7
|
function errorTracking(options = {}) {
|
|
8
|
-
return {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
...options,
|
|
13
|
-
siteKey: context.siteKey,
|
|
14
|
-
baseUrl: context.baseUrl,
|
|
15
|
-
debug: context.debug,
|
|
16
|
-
trackHash: context.trackHash,
|
|
17
|
-
cookieless: context.isCookieless(),
|
|
18
|
-
sdkName: context.sdkName,
|
|
19
|
-
sdkVersion: context.sdkVersion
|
|
20
|
-
});
|
|
21
|
-
context.on("pageHide", ({ persisted }) => tracker.onPageHidden(persisted));
|
|
22
|
-
context.on("error", ({ error }) => tracker.captureError(error));
|
|
23
|
-
context.on("consentChange", ({ cookieless }) => tracker.setCookielessMode(cookieless));
|
|
24
|
-
tracker.start();
|
|
25
|
-
return ({ discard }) => tracker.stop(discard);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
8
|
+
return trackerExtension("error-tracking", (context) => new ErrorTracker({
|
|
9
|
+
...options,
|
|
10
|
+
...context
|
|
11
|
+
}), (tracker, context) => context.on("error", ({ error }) => tracker.captureError(error)));
|
|
28
12
|
}
|
|
29
13
|
const EXTENSION_URL = /(?:^|\()(chrome|moz|ms-browser|safari-web)-extension:\/\//;
|
|
30
14
|
function toEntry(kind, source, handled, fallback) {
|
|
@@ -103,23 +87,34 @@ var ErrorTracker = class {
|
|
|
103
87
|
const buildId = globalThis.__SOURCEMAPS_BUILD__?.buildId;
|
|
104
88
|
const userId = getAnonymousId(this.options.siteKey, this.cookieless);
|
|
105
89
|
const ctx = getSessionContext(this.options.siteKey, this.cookieless);
|
|
106
|
-
|
|
90
|
+
const context = {
|
|
91
|
+
token: this.options.siteKey,
|
|
92
|
+
...userId ? { userId } : {},
|
|
93
|
+
sessionId: ctx.sessionId,
|
|
94
|
+
windowId: ctx.windowId,
|
|
95
|
+
...typeof buildId === "string" && buildId.trim() ? { buildId } : {},
|
|
96
|
+
sdkName: this.options.sdkName ?? SDK_NAME,
|
|
97
|
+
sdkVersion: this.options.sdkVersion ?? SDK_VERSION,
|
|
98
|
+
event: "error",
|
|
99
|
+
...getPageContext(this.options.trackHash ?? false),
|
|
100
|
+
properties: {}
|
|
101
|
+
};
|
|
102
|
+
const key = JSON.stringify([context, error]);
|
|
103
|
+
if (this.add({
|
|
104
|
+
key,
|
|
105
|
+
context,
|
|
107
106
|
error,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
...userId ? { userId } : {},
|
|
111
|
-
sessionId: ctx.sessionId,
|
|
112
|
-
windowId: ctx.windowId,
|
|
113
|
-
...typeof buildId === "string" && buildId.trim() ? { buildId } : {},
|
|
114
|
-
sdkName: this.options.sdkName ?? SDK_NAME,
|
|
115
|
-
sdkVersion: this.options.sdkVersion ?? SDK_VERSION,
|
|
116
|
-
event: "error",
|
|
117
|
-
...getPageContext(this.options.trackHash ?? false),
|
|
118
|
-
properties: {}
|
|
119
|
-
}
|
|
120
|
-
});
|
|
107
|
+
count: 1
|
|
108
|
+
})) return;
|
|
121
109
|
if (this.queue.length >= this.maxQueueSize) this.flush();
|
|
122
110
|
}
|
|
111
|
+
/** Returns true when the error was already queued. */
|
|
112
|
+
add(item) {
|
|
113
|
+
const queued = this.queue.find(({ key }) => key === item.key);
|
|
114
|
+
if (queued) queued.count += item.count;
|
|
115
|
+
else this.queue.push(item);
|
|
116
|
+
return !!queued;
|
|
117
|
+
}
|
|
123
118
|
async flush() {
|
|
124
119
|
if (this.flushing || !this.queue.length) return;
|
|
125
120
|
this.flushing = true;
|
|
@@ -129,44 +124,36 @@ var ErrorTracker = class {
|
|
|
129
124
|
const groups = /* @__PURE__ */ new Map();
|
|
130
125
|
for (const item of batch) {
|
|
131
126
|
const key = JSON.stringify(item.context);
|
|
132
|
-
const group = groups.get(key)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
};
|
|
136
|
-
group.items.push(item);
|
|
137
|
-
groups.set(key, group);
|
|
127
|
+
const group = groups.get(key);
|
|
128
|
+
if (group) group.push(item);
|
|
129
|
+
else groups.set(key, [item]);
|
|
138
130
|
}
|
|
139
131
|
let failed = [];
|
|
140
132
|
try {
|
|
141
|
-
failed = (await Promise.all([...groups.values()].map(async (
|
|
142
|
-
const errors = /* @__PURE__ */ new Map();
|
|
143
|
-
for (const { error } of group.items) {
|
|
144
|
-
const key = JSON.stringify(error);
|
|
145
|
-
const existing = errors.get(key);
|
|
146
|
-
if (existing) existing.count++;
|
|
147
|
-
else errors.set(key, {
|
|
148
|
-
...error,
|
|
149
|
-
count: 1
|
|
150
|
-
});
|
|
151
|
-
}
|
|
133
|
+
failed = (await Promise.all([...groups.values()].map(async (items) => {
|
|
152
134
|
return await sendData({
|
|
153
135
|
url: this.endpoint,
|
|
154
136
|
data: JSON.stringify({
|
|
155
|
-
...
|
|
156
|
-
errors:
|
|
137
|
+
...items[0]?.context,
|
|
138
|
+
errors: items.map(({ error, count }) => ({
|
|
139
|
+
...error,
|
|
140
|
+
count
|
|
141
|
+
}))
|
|
157
142
|
}),
|
|
158
143
|
debug: this.options.debug,
|
|
159
144
|
debugPrefix: "[ErrorTracker]",
|
|
160
145
|
settleRejected: true
|
|
161
|
-
}) ? [] :
|
|
146
|
+
}) ? [] : items;
|
|
162
147
|
}))).flat();
|
|
163
148
|
} finally {
|
|
164
149
|
this.flushing = false;
|
|
165
150
|
}
|
|
166
151
|
if (generation !== this.generation) return;
|
|
167
|
-
|
|
152
|
+
const captured = this.queue;
|
|
153
|
+
this.queue = [...failed];
|
|
154
|
+
for (const item of captured) this.add(item);
|
|
155
|
+
this.queue = this.queue.slice(-this.maxQueueSize);
|
|
168
156
|
if (!this.started && !failed.length) this.flush();
|
|
169
157
|
}
|
|
170
158
|
};
|
|
171
|
-
//#endregion
|
|
172
159
|
export { ErrorTracker as default, errorTracking };
|
package/dist/flags.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { n as AnalyticsExtensionContext, r as ExtensionCleanup, t as AnalyticsExtension } from "./chunks/extensions-
|
|
2
|
-
//#region src/flags.d.ts
|
|
1
|
+
import { n as AnalyticsExtensionContext, r as ExtensionCleanup, t as AnalyticsExtension } from "./chunks/extensions-C2k9Im6I.js";
|
|
3
2
|
export interface FeatureFlagsOptions {
|
|
4
3
|
/**
|
|
5
4
|
* Flag keys this app reads. Every loaded flag is recorded as an exposure,
|
|
@@ -75,5 +74,4 @@ export declare class FeatureFlags implements AnalyticsExtension {
|
|
|
75
74
|
private evaluate;
|
|
76
75
|
private update;
|
|
77
76
|
}
|
|
78
|
-
export declare function featureFlags(options?: FeatureFlagsOptions): FeatureFlags;
|
|
79
|
-
//#endregion
|
|
77
|
+
export declare function featureFlags(options?: FeatureFlagsOptions): FeatureFlags;
|
package/dist/flags.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as getSessionContext } from "./chunks/session-manager-
|
|
2
|
-
import { t as getAnonymousId } from "./chunks/identifiers-
|
|
3
|
-
|
|
1
|
+
import { n as getSessionContext } from "./chunks/session-manager-DBWYTyfd.js";
|
|
2
|
+
import { t as getAnonymousId } from "./chunks/identifiers-BHYhM7DM.js";
|
|
3
|
+
// src/flags.ts
|
|
4
4
|
const DEFAULT_ENDPOINT = "https://flags.faststats.dev";
|
|
5
5
|
const DEFAULT_TIMEOUT_MS = 3e3;
|
|
6
6
|
/**
|
|
@@ -141,5 +141,4 @@ var FeatureFlags = class {
|
|
|
141
141
|
function featureFlags(options) {
|
|
142
142
|
return new FeatureFlags(options);
|
|
143
143
|
}
|
|
144
|
-
//#endregion
|
|
145
144
|
export { FeatureFlags, featureFlags };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { a as ExtensionHook, i as ExtensionHandler, n as AnalyticsExtensionContext, o as ExtensionHooks, r as ExtensionCleanup, t as AnalyticsExtension } from "./chunks/extensions-
|
|
2
|
-
//#region src/analytics.d.ts
|
|
1
|
+
import { a as ExtensionHook, i as ExtensionHandler, n as AnalyticsExtensionContext, o as ExtensionHooks, r as ExtensionCleanup, t as AnalyticsExtension } from "./chunks/extensions-C2k9Im6I.js";
|
|
3
2
|
type ConsentMode = "anonymous" | "granted" | "denied";
|
|
4
3
|
interface IdentifyOptions {
|
|
5
4
|
name?: string | null;
|
|
@@ -66,20 +65,15 @@ export declare class WebAnalytics {
|
|
|
66
65
|
private readonly onPageHide;
|
|
67
66
|
private readonly onPageShow;
|
|
68
67
|
private readonly onResume;
|
|
68
|
+
private readonly onBlur;
|
|
69
69
|
private readonly onActivity;
|
|
70
70
|
private isIdle;
|
|
71
71
|
private leavePage;
|
|
72
|
-
/**
|
|
73
|
-
* Reports the visit's running totals: as a `page_ping` while it may still
|
|
74
|
-
* continue, and as its single `page_leave` once it has ended.
|
|
75
|
-
*/
|
|
76
72
|
private checkpoint;
|
|
77
73
|
private pauseVisit;
|
|
78
74
|
private resumeVisit;
|
|
79
75
|
private send;
|
|
80
76
|
}
|
|
81
|
-
//#endregion
|
|
82
|
-
//#region src/client.d.ts
|
|
83
77
|
/** Creates an independent client. Call `start()` when it should begin tracking. */
|
|
84
78
|
export declare function createClient(options: WebAnalyticsOptions): WebAnalytics;
|
|
85
79
|
/** Initializes and starts the shared client used by the convenience functions. */
|
|
@@ -93,5 +87,4 @@ export declare function identify(externalId: string, email?: string, options?: I
|
|
|
93
87
|
export declare function logout(resetAnonymousIdentity?: boolean): void;
|
|
94
88
|
export declare function setConsentMode(mode: ConsentMode): void;
|
|
95
89
|
export declare function reportError(error: Error): void;
|
|
96
|
-
//#endregion
|
|
97
90
|
export type { AnalyticsExtension, AnalyticsExtensionContext, ConsentMode, ExtensionCleanup, ExtensionHandler, ExtensionHook, ExtensionHooks, IdentifyOptions, IdentifyUser, WebAnalyticsOptions };
|