@faststats/web 0.13.0 → 0.13.1
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/identifiers-3X8XI1pD.js +16 -0
- package/dist/chunks/send-data-B7P3f5PT.js +177 -0
- package/dist/chunks/session-manager-VM5RZj9H.js +116 -0
- package/dist/error.d.ts +2 -0
- package/dist/error.js +47 -59
- package/dist/flags.js +2 -2
- package/dist/index.d.ts +1 -4
- package/dist/index.js +51 -121
- package/dist/replay.d.ts +37 -64
- package/dist/replay.js +423 -439
- package/dist/web-vitals.js +3 -18
- 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
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { o as getStorageItem, s as setStorageItem, t as createId } from "./session-manager-VM5RZj9H.js";
|
|
2
|
+
//#region 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
|
+
//#endregion
|
|
16
|
+
export { resetAnonymousId as n, getAnonymousId as t };
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
//#region 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
|
+
//#endregion
|
|
80
|
+
//#region src/utils/api-urls.ts
|
|
81
|
+
function resolveBaseUrl(baseUrl) {
|
|
82
|
+
return baseUrl?.replace(/\/+$/, "") || "https://metrics.faststats.dev";
|
|
83
|
+
}
|
|
84
|
+
const URLS = {
|
|
85
|
+
events: "/v1/web",
|
|
86
|
+
identify: "/v1/identify",
|
|
87
|
+
replay: "/v1/replay",
|
|
88
|
+
vitals: "/v1/vitals"
|
|
89
|
+
};
|
|
90
|
+
function sanitizeUrl(url, includeHash = false) {
|
|
91
|
+
try {
|
|
92
|
+
const parsed = new URL(url, location.href);
|
|
93
|
+
return `${parsed.origin}${parsed.pathname}${includeHash ? parsed.hash : ""}`;
|
|
94
|
+
} catch {
|
|
95
|
+
return url.split(includeHash ? "?" : /[?#]/, 1)[0] ?? "";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const UTM_KEYS = [
|
|
99
|
+
"utm_source",
|
|
100
|
+
"utm_medium",
|
|
101
|
+
"utm_campaign",
|
|
102
|
+
"utm_term",
|
|
103
|
+
"utm_content"
|
|
104
|
+
];
|
|
105
|
+
function getReferrer() {
|
|
106
|
+
const raw = document.referrer;
|
|
107
|
+
if (!raw) return null;
|
|
108
|
+
try {
|
|
109
|
+
if (new URL(raw, location.href).origin === location.origin) return null;
|
|
110
|
+
} catch {}
|
|
111
|
+
return sanitizeUrl(raw);
|
|
112
|
+
}
|
|
113
|
+
/** Visitor environment sent with every event; unavailable values are omitted. */
|
|
114
|
+
function getClientContext() {
|
|
115
|
+
let timezone;
|
|
116
|
+
try {
|
|
117
|
+
timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
118
|
+
} catch {}
|
|
119
|
+
const { width, height } = globalThis.screen ?? {};
|
|
120
|
+
return {
|
|
121
|
+
timezone,
|
|
122
|
+
language: globalThis.navigator?.language,
|
|
123
|
+
screen: width && height ? `${width}x${height}` : void 0
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function getPageContext(includeHash = false) {
|
|
127
|
+
const hash = includeHash ? location.hash : "";
|
|
128
|
+
const ctx = {
|
|
129
|
+
page: `${location.pathname}${hash}`,
|
|
130
|
+
url: sanitizeUrl(location.href, includeHash),
|
|
131
|
+
referrer: getReferrer(),
|
|
132
|
+
title: document.title
|
|
133
|
+
};
|
|
134
|
+
if (!location.search) return ctx;
|
|
135
|
+
const sp = new URLSearchParams(location.search);
|
|
136
|
+
for (const key of UTM_KEYS) {
|
|
137
|
+
const value = sp.get(key);
|
|
138
|
+
if (value) ctx[key] = value;
|
|
139
|
+
}
|
|
140
|
+
return ctx;
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/utils/send-data.ts
|
|
144
|
+
async function sendData(options) {
|
|
145
|
+
const { url, data, contentType = "application/json", debug = false, debugPrefix = "[Analytics]", useBeacon = true, keepalive = true, settleRejected } = options;
|
|
146
|
+
const log = (...details) => {
|
|
147
|
+
if (debug) console.log(debugPrefix, ...details);
|
|
148
|
+
};
|
|
149
|
+
if (useBeacon) try {
|
|
150
|
+
const body = data instanceof Blob ? data : new Blob([data], { type: contentType });
|
|
151
|
+
if (navigator.sendBeacon(url, body)) {
|
|
152
|
+
log("Sent via beacon");
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
} catch {}
|
|
156
|
+
const post = (keepalive) => fetch(url, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
body: data,
|
|
159
|
+
headers: { "Content-Type": contentType },
|
|
160
|
+
keepalive,
|
|
161
|
+
credentials: "omit"
|
|
162
|
+
});
|
|
163
|
+
try {
|
|
164
|
+
const useKeepalive = keepalive && new Blob([data]).size <= 61440;
|
|
165
|
+
const { ok, status } = await post(useKeepalive).catch((error) => {
|
|
166
|
+
if (!useKeepalive) throw error;
|
|
167
|
+
return post(false);
|
|
168
|
+
});
|
|
169
|
+
log(ok ? "Sent via fetch" : status);
|
|
170
|
+
return ok || !!settleRejected && status < 500 && status !== 429;
|
|
171
|
+
} catch {
|
|
172
|
+
log("Failed to send");
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
177
|
+
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,116 @@
|
|
|
1
|
+
//#region 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
|
+
//#endregion
|
|
22
|
+
//#region src/utils/session-manager.ts
|
|
23
|
+
const IDLE_MS = 18e5;
|
|
24
|
+
const MAX_AGE_MS = 864e5;
|
|
25
|
+
const SESSION_KEY = "faststats_session_";
|
|
26
|
+
const WINDOW_KEY = "faststats_window_id_";
|
|
27
|
+
const memory = /* @__PURE__ */ new Map();
|
|
28
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
29
|
+
const NO_STORAGE = {};
|
|
30
|
+
const slot = (siteKey, cookieless) => `${siteKey}:${cookieless ? "memory" : "persistent"}`;
|
|
31
|
+
function storageScope() {
|
|
32
|
+
try {
|
|
33
|
+
return globalThis.localStorage ?? NO_STORAGE;
|
|
34
|
+
} catch {
|
|
35
|
+
return NO_STORAGE;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function createId() {
|
|
39
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
|
|
40
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
41
|
+
}
|
|
42
|
+
function load(siteKey, cookieless) {
|
|
43
|
+
if (!cookieless) try {
|
|
44
|
+
const session = JSON.parse(getStorageItem("localStorage", `${SESSION_KEY}${siteKey}`) ?? "null");
|
|
45
|
+
if (typeof session?.id === "string" && session.id && Number.isFinite(session.start) && Number.isFinite(session.activity)) return session;
|
|
46
|
+
} catch {}
|
|
47
|
+
const entry = memory.get(slot(siteKey, cookieless));
|
|
48
|
+
return entry?.scope === storageScope() ? entry.session : null;
|
|
49
|
+
}
|
|
50
|
+
function save(siteKey, cookieless, session) {
|
|
51
|
+
const json = JSON.stringify(session);
|
|
52
|
+
if (cookieless || !setStorageItem("localStorage", SESSION_KEY + siteKey, json)) memory.set(slot(siteKey, cookieless), {
|
|
53
|
+
scope: storageScope(),
|
|
54
|
+
session
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function toContext(siteKey, cookieless, { id, start }) {
|
|
58
|
+
let windowId = cookieless ? id : getStorageItem("sessionStorage", WINDOW_KEY + siteKey);
|
|
59
|
+
if (!windowId) {
|
|
60
|
+
windowId = createId();
|
|
61
|
+
if (!setStorageItem("sessionStorage", WINDOW_KEY + siteKey, windowId)) windowId = id;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
sessionId: id,
|
|
65
|
+
windowId,
|
|
66
|
+
sessionStart: start
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function emit(siteKey, cookieless, prev, next) {
|
|
70
|
+
for (const listener of listeners.get(slot(siteKey, cookieless)) ?? []) listener(prev, next);
|
|
71
|
+
}
|
|
72
|
+
function resolveSession(siteKey, cookieless, touch) {
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
const existing = load(siteKey, cookieless);
|
|
75
|
+
const expired = !!existing && (now - existing.activity >= IDLE_MS || now - existing.start >= MAX_AGE_MS);
|
|
76
|
+
const session = existing && !expired ? existing : {
|
|
77
|
+
id: createId(),
|
|
78
|
+
start: now,
|
|
79
|
+
activity: now
|
|
80
|
+
};
|
|
81
|
+
if (touch) session.activity = now;
|
|
82
|
+
if (touch || session !== existing) save(siteKey, cookieless, session);
|
|
83
|
+
const context = toContext(siteKey, cookieless, session);
|
|
84
|
+
if (existing && expired) emit(siteKey, cookieless, toContext(siteKey, cookieless, existing), context);
|
|
85
|
+
return context;
|
|
86
|
+
}
|
|
87
|
+
/** The current session, replacing an expired one without counting activity. */
|
|
88
|
+
function getSessionContext(siteKey, cookieless = false) {
|
|
89
|
+
return resolveSession(siteKey, cookieless, false);
|
|
90
|
+
}
|
|
91
|
+
/** Counts visitor activity in the current session, which it returns. */
|
|
92
|
+
function touchActivity(siteKey, cookieless = false) {
|
|
93
|
+
return resolveSession(siteKey, cookieless, true);
|
|
94
|
+
}
|
|
95
|
+
function resetSession(siteKey, cookieless = false) {
|
|
96
|
+
const prev = getSessionContext(siteKey, cookieless);
|
|
97
|
+
memory.delete(slot(siteKey, cookieless));
|
|
98
|
+
if (!cookieless) {
|
|
99
|
+
setStorageItem("localStorage", SESSION_KEY + siteKey, null);
|
|
100
|
+
setStorageItem("sessionStorage", WINDOW_KEY + siteKey, null);
|
|
101
|
+
}
|
|
102
|
+
const next = getSessionContext(siteKey, cookieless);
|
|
103
|
+
if (prev.sessionId !== next.sessionId) emit(siteKey, cookieless, prev, next);
|
|
104
|
+
return next;
|
|
105
|
+
}
|
|
106
|
+
function onSessionRotated(siteKey, cookieless, listener) {
|
|
107
|
+
const key = slot(siteKey, cookieless);
|
|
108
|
+
const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
|
|
109
|
+
listeners.set(key, set.add(listener));
|
|
110
|
+
return () => {
|
|
111
|
+
set.delete(listener);
|
|
112
|
+
if (!set.size) listeners.delete(key);
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
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
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-
|
|
1
|
+
import { a as resolveBaseUrl, c as trackerExtension, i as getPageContext, n as URLS, t as sendData } from "./chunks/send-data-B7P3f5PT.js";
|
|
2
|
+
import { n as getSessionContext } from "./chunks/session-manager-VM5RZj9H.js";
|
|
3
|
+
import { t as getAnonymousId } from "./chunks/identifiers-3X8XI1pD.js";
|
|
4
4
|
//#region src/error.ts
|
|
5
5
|
const SDK_NAME = "@faststats/web";
|
|
6
|
-
const SDK_VERSION = "0.13.
|
|
6
|
+
const SDK_VERSION = "0.13.1";
|
|
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,42 +124,35 @@ 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
|
};
|
package/dist/flags.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as getSessionContext } from "./chunks/session-manager-
|
|
2
|
-
import { t as getAnonymousId } from "./chunks/identifiers-
|
|
1
|
+
import { n as getSessionContext } from "./chunks/session-manager-VM5RZj9H.js";
|
|
2
|
+
import { t as getAnonymousId } from "./chunks/identifiers-3X8XI1pD.js";
|
|
3
3
|
//#region src/flags.ts
|
|
4
4
|
const DEFAULT_ENDPOINT = "https://flags.faststats.dev";
|
|
5
5
|
const DEFAULT_TIMEOUT_MS = 3e3;
|
package/dist/index.d.ts
CHANGED
|
@@ -66,13 +66,10 @@ export declare class WebAnalytics {
|
|
|
66
66
|
private readonly onPageHide;
|
|
67
67
|
private readonly onPageShow;
|
|
68
68
|
private readonly onResume;
|
|
69
|
+
private readonly onBlur;
|
|
69
70
|
private readonly onActivity;
|
|
70
71
|
private isIdle;
|
|
71
72
|
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
73
|
private checkpoint;
|
|
77
74
|
private pauseVisit;
|
|
78
75
|
private resumeVisit;
|