@prism-analytics/browser 0.0.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/README.md +40 -0
- package/dist/index.d.mts +170 -0
- package/dist/index.d.ts +170 -0
- package/dist/index.js +821 -0
- package/dist/index.mjs +804 -0
- package/package.json +40 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
var __async = (__this, __arguments, generator) => {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
var fulfilled = (value) => {
|
|
23
|
+
try {
|
|
24
|
+
step(generator.next(value));
|
|
25
|
+
} catch (e) {
|
|
26
|
+
reject(e);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var rejected = (value) => {
|
|
30
|
+
try {
|
|
31
|
+
step(generator.throw(value));
|
|
32
|
+
} catch (e) {
|
|
33
|
+
reject(e);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
37
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/index.ts
|
|
42
|
+
import {
|
|
43
|
+
createPrismClient
|
|
44
|
+
} from "@prism-analytics/core";
|
|
45
|
+
|
|
46
|
+
// src/browser-runtime.ts
|
|
47
|
+
function browserCreateId() {
|
|
48
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
49
|
+
return crypto.randomUUID();
|
|
50
|
+
}
|
|
51
|
+
return `prism-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
|
|
52
|
+
}
|
|
53
|
+
function createBrowserStorage() {
|
|
54
|
+
try {
|
|
55
|
+
const probe = "__prism_storage_probe__";
|
|
56
|
+
window.sessionStorage.setItem(probe, "1");
|
|
57
|
+
window.sessionStorage.removeItem(probe);
|
|
58
|
+
window.localStorage.setItem(probe, "1");
|
|
59
|
+
window.localStorage.removeItem(probe);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
const storeFor = (key) => (
|
|
64
|
+
// R3-F2: queue snapshots, session globals, and SESSION-scoped identity
|
|
65
|
+
// live in sessionStorage (per execution context); persistent-scope
|
|
66
|
+
// identity and globals live in localStorage (cross-launch).
|
|
67
|
+
key.startsWith("prism:queue:") || key.includes(":globals:session:") || key.includes("prism:identity:session:") ? window.sessionStorage : window.localStorage
|
|
68
|
+
);
|
|
69
|
+
return {
|
|
70
|
+
getItem: (key) => __async(null, null, function* () {
|
|
71
|
+
try {
|
|
72
|
+
return storeFor(key).getItem(key);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}),
|
|
77
|
+
setItem: (key, value) => __async(null, null, function* () {
|
|
78
|
+
storeFor(key).setItem(key, value);
|
|
79
|
+
}),
|
|
80
|
+
removeItem: (key) => __async(null, null, function* () {
|
|
81
|
+
storeFor(key).removeItem(key);
|
|
82
|
+
})
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function createBrowserLifecycle() {
|
|
86
|
+
const listeners = {
|
|
87
|
+
foreground: /* @__PURE__ */ new Set(),
|
|
88
|
+
background: /* @__PURE__ */ new Set(),
|
|
89
|
+
"before-unload": /* @__PURE__ */ new Set()
|
|
90
|
+
};
|
|
91
|
+
const fire = (event) => {
|
|
92
|
+
for (const listener of listeners[event]) listener();
|
|
93
|
+
};
|
|
94
|
+
const onVisibility = () => {
|
|
95
|
+
fire(document.visibilityState === "visible" ? "foreground" : "background");
|
|
96
|
+
};
|
|
97
|
+
const onBeforeUnload = () => fire("before-unload");
|
|
98
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
99
|
+
window.addEventListener("beforeunload", onBeforeUnload);
|
|
100
|
+
return {
|
|
101
|
+
on(event, listener) {
|
|
102
|
+
listeners[event].add(listener);
|
|
103
|
+
let removed = false;
|
|
104
|
+
return () => {
|
|
105
|
+
if (removed) return;
|
|
106
|
+
removed = true;
|
|
107
|
+
listeners[event].delete(listener);
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function captureBrowserContext() {
|
|
113
|
+
var _a, _b;
|
|
114
|
+
const context = {
|
|
115
|
+
platform: "browser",
|
|
116
|
+
kind: "web"
|
|
117
|
+
};
|
|
118
|
+
const width = window.innerWidth || ((_a = window.screen) == null ? void 0 : _a.width) || 0;
|
|
119
|
+
const height = window.innerHeight || ((_b = window.screen) == null ? void 0 : _b.height) || 0;
|
|
120
|
+
if (width > 0 && height > 0) {
|
|
121
|
+
context.screenSize = { width, height };
|
|
122
|
+
}
|
|
123
|
+
const locale = navigator.language;
|
|
124
|
+
if (locale) context.locale = locale;
|
|
125
|
+
try {
|
|
126
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
127
|
+
if (timezone) context.timezone = timezone;
|
|
128
|
+
} catch (e) {
|
|
129
|
+
}
|
|
130
|
+
return context;
|
|
131
|
+
}
|
|
132
|
+
function capturePageContext() {
|
|
133
|
+
let path = "/";
|
|
134
|
+
try {
|
|
135
|
+
path = window.location.pathname || "/";
|
|
136
|
+
} catch (e) {
|
|
137
|
+
}
|
|
138
|
+
let referrer = null;
|
|
139
|
+
try {
|
|
140
|
+
const raw = document.referrer;
|
|
141
|
+
if (raw) referrer = new URL(raw).origin;
|
|
142
|
+
} catch (e) {
|
|
143
|
+
referrer = null;
|
|
144
|
+
}
|
|
145
|
+
return { path, referrer };
|
|
146
|
+
}
|
|
147
|
+
function createBrowserRuntime() {
|
|
148
|
+
const storage = createBrowserStorage();
|
|
149
|
+
return __spreadValues({
|
|
150
|
+
name: "browser",
|
|
151
|
+
now: () => Date.now(),
|
|
152
|
+
createId: browserCreateId,
|
|
153
|
+
transport: {
|
|
154
|
+
post: (url, request) => {
|
|
155
|
+
const controller = new AbortController();
|
|
156
|
+
const onCoreAbort = () => controller.abort();
|
|
157
|
+
request.signal.addEventListener("abort", onCoreAbort);
|
|
158
|
+
const timer = window.setTimeout(
|
|
159
|
+
() => controller.abort(),
|
|
160
|
+
request.timeoutMs
|
|
161
|
+
);
|
|
162
|
+
const bodyBytes = new TextEncoder().encode(request.body).length;
|
|
163
|
+
const keepalive = bodyBytes <= 64 * 1024;
|
|
164
|
+
return fetch(url, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: __spreadValues({}, request.headers),
|
|
167
|
+
body: request.body,
|
|
168
|
+
signal: controller.signal,
|
|
169
|
+
keepalive
|
|
170
|
+
}).then((response) => __async(null, null, function* () {
|
|
171
|
+
return {
|
|
172
|
+
status: response.status,
|
|
173
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
174
|
+
text: () => response.text()
|
|
175
|
+
};
|
|
176
|
+
})).finally(() => {
|
|
177
|
+
window.clearTimeout(timer);
|
|
178
|
+
request.signal.removeEventListener("abort", onCoreAbort);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
schedule: (delayMs, callback) => {
|
|
183
|
+
const handle = window.setTimeout(callback, delayMs);
|
|
184
|
+
return () => window.clearTimeout(handle);
|
|
185
|
+
},
|
|
186
|
+
context: captureBrowserContext(),
|
|
187
|
+
lifecycle: createBrowserLifecycle()
|
|
188
|
+
}, storage ? { storage } : {});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/page-tracker.ts
|
|
192
|
+
import {
|
|
193
|
+
INTERNAL_SEAM,
|
|
194
|
+
PAGE_VIEW_EVENT_NAME,
|
|
195
|
+
PAGE_VIEW_LIMITS
|
|
196
|
+
} from "@prism-analytics/core";
|
|
197
|
+
function dropped(reason) {
|
|
198
|
+
return { status: "dropped", reason };
|
|
199
|
+
}
|
|
200
|
+
function hashString(input) {
|
|
201
|
+
let h = 2166136261;
|
|
202
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
203
|
+
h ^= input.charCodeAt(i);
|
|
204
|
+
h = Math.imul(h, 16777619);
|
|
205
|
+
}
|
|
206
|
+
return (h >>> 0).toString(36);
|
|
207
|
+
}
|
|
208
|
+
function safeSessionStorage() {
|
|
209
|
+
try {
|
|
210
|
+
const probe = "__prism_probe__";
|
|
211
|
+
window.sessionStorage.setItem(probe, "1");
|
|
212
|
+
window.sessionStorage.removeItem(probe);
|
|
213
|
+
return window.sessionStorage;
|
|
214
|
+
} catch (e) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function stableId() {
|
|
219
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
220
|
+
return crypto.randomUUID();
|
|
221
|
+
}
|
|
222
|
+
return `wsess-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
223
|
+
}
|
|
224
|
+
var historyPatch = null;
|
|
225
|
+
function notify(navigation) {
|
|
226
|
+
if (!historyPatch) return;
|
|
227
|
+
for (const listener of [...historyPatch.listeners]) {
|
|
228
|
+
try {
|
|
229
|
+
listener(navigation);
|
|
230
|
+
} catch (e) {
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function acquireHistoryPatch(listener) {
|
|
235
|
+
if (!historyPatch) {
|
|
236
|
+
const originalPush = window.history.pushState.bind(window.history);
|
|
237
|
+
const originalReplace = window.history.replaceState.bind(window.history);
|
|
238
|
+
const patch = {
|
|
239
|
+
count: 0,
|
|
240
|
+
originalPush,
|
|
241
|
+
originalReplace,
|
|
242
|
+
listeners: /* @__PURE__ */ new Set()
|
|
243
|
+
};
|
|
244
|
+
window.history.pushState = function patchedPush(...args) {
|
|
245
|
+
const result = originalPush.apply(this, args);
|
|
246
|
+
notify("push");
|
|
247
|
+
return result;
|
|
248
|
+
};
|
|
249
|
+
window.history.replaceState = function patchedReplace(...args) {
|
|
250
|
+
const result = originalReplace.apply(this, args);
|
|
251
|
+
notify("replace");
|
|
252
|
+
return result;
|
|
253
|
+
};
|
|
254
|
+
window.addEventListener("popstate", () => notify("pop"));
|
|
255
|
+
historyPatch = patch;
|
|
256
|
+
}
|
|
257
|
+
historyPatch.count += 1;
|
|
258
|
+
historyPatch.listeners.add(listener);
|
|
259
|
+
return () => {
|
|
260
|
+
const current = historyPatch;
|
|
261
|
+
if (!current) return;
|
|
262
|
+
current.listeners.delete(listener);
|
|
263
|
+
current.count -= 1;
|
|
264
|
+
if (current.count <= 0) {
|
|
265
|
+
window.history.pushState = current.originalPush;
|
|
266
|
+
window.history.replaceState = current.originalReplace;
|
|
267
|
+
historyPatch = null;
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function createPageTracker(args) {
|
|
272
|
+
var _a;
|
|
273
|
+
const { client, options } = args;
|
|
274
|
+
const seam = (_a = client[INTERNAL_SEAM]) != null ? _a : null;
|
|
275
|
+
const storage = safeSessionStorage();
|
|
276
|
+
const stateKey = `prism:web-session:${hashString(args.sourceKey)}:${hashString(args.endpoint)}`;
|
|
277
|
+
let disposed = false;
|
|
278
|
+
let removeHistory = null;
|
|
279
|
+
let lastCapturedPath = null;
|
|
280
|
+
let lastManualSignature = null;
|
|
281
|
+
let lastManualAt = 0;
|
|
282
|
+
function loadState() {
|
|
283
|
+
if (!storage) return null;
|
|
284
|
+
try {
|
|
285
|
+
const raw = storage.getItem(stateKey);
|
|
286
|
+
if (!raw) return null;
|
|
287
|
+
const parsed = JSON.parse(raw);
|
|
288
|
+
if ((parsed == null ? void 0 : parsed.v) !== 1 || !parsed.sessionId) return null;
|
|
289
|
+
return parsed;
|
|
290
|
+
} catch (e) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function saveState(state) {
|
|
295
|
+
if (!storage) return;
|
|
296
|
+
try {
|
|
297
|
+
storage.setItem(stateKey, JSON.stringify(state));
|
|
298
|
+
} catch (e) {
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function clearState() {
|
|
302
|
+
if (!storage) return;
|
|
303
|
+
try {
|
|
304
|
+
storage.removeItem(stateKey);
|
|
305
|
+
} catch (e) {
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function ensureWebSession(now) {
|
|
309
|
+
const stored = loadState();
|
|
310
|
+
if (stored && now - stored.lastActivityAt <= PAGE_VIEW_LIMITS.webSessionInactivityTimeoutMs) {
|
|
311
|
+
seam == null ? void 0 : seam.resumeWebSession({
|
|
312
|
+
sessionId: stored.sessionId,
|
|
313
|
+
startedAt: stored.startedAt
|
|
314
|
+
});
|
|
315
|
+
const sequence = stored.sequence + 1;
|
|
316
|
+
saveState(__spreadProps(__spreadValues({}, stored), { lastActivityAt: now, sequence }));
|
|
317
|
+
return sequence;
|
|
318
|
+
}
|
|
319
|
+
const handle = client.startSession();
|
|
320
|
+
const sessionId = handle.status === "started" ? handle.session.sessionId : stableId();
|
|
321
|
+
const startedAt = now;
|
|
322
|
+
saveState({
|
|
323
|
+
v: 1,
|
|
324
|
+
sessionId,
|
|
325
|
+
startedAt,
|
|
326
|
+
lastActivityAt: now,
|
|
327
|
+
sequence: 1
|
|
328
|
+
});
|
|
329
|
+
return 1;
|
|
330
|
+
}
|
|
331
|
+
function buildCandidate(navigation, override) {
|
|
332
|
+
var _a2;
|
|
333
|
+
let host = "";
|
|
334
|
+
let path = "/";
|
|
335
|
+
try {
|
|
336
|
+
host = window.location.hostname.toLowerCase();
|
|
337
|
+
path = window.location.pathname || "/";
|
|
338
|
+
} catch (e) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
if ((override == null ? void 0 : override.path) && override.path.startsWith("/")) {
|
|
342
|
+
path = override.path;
|
|
343
|
+
}
|
|
344
|
+
if (!host || !isValidPath(path)) return null;
|
|
345
|
+
const stored = loadState();
|
|
346
|
+
const candidate = __spreadValues(__spreadValues({
|
|
347
|
+
host,
|
|
348
|
+
path,
|
|
349
|
+
navigation,
|
|
350
|
+
sequence: stored ? stored.sequence + 1 : 1
|
|
351
|
+
}, stored && lastCapturedPath ? { previousPath: lastCapturedPath } : {}), options.captureTitle ? {
|
|
352
|
+
title: ((_a2 = override == null ? void 0 : override.title) != null ? _a2 : documentTitle()).slice(
|
|
353
|
+
0,
|
|
354
|
+
PAGE_VIEW_LIMITS.maxTitleLength
|
|
355
|
+
)
|
|
356
|
+
} : {});
|
|
357
|
+
if (typeof options.beforeCapture === "function") {
|
|
358
|
+
try {
|
|
359
|
+
const next = options.beforeCapture(candidate);
|
|
360
|
+
if (!next) return null;
|
|
361
|
+
return next;
|
|
362
|
+
} catch (e) {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return candidate;
|
|
367
|
+
}
|
|
368
|
+
function isValidPath(path) {
|
|
369
|
+
return path.startsWith("/") && path.length <= PAGE_VIEW_LIMITS.maxPathLength && !path.includes("?") && !path.includes("#");
|
|
370
|
+
}
|
|
371
|
+
function documentTitle() {
|
|
372
|
+
var _a2;
|
|
373
|
+
try {
|
|
374
|
+
return typeof document !== "undefined" ? (_a2 = document.title) != null ? _a2 : "" : "";
|
|
375
|
+
} catch (e) {
|
|
376
|
+
return "";
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function externalReferrerHost(host) {
|
|
380
|
+
try {
|
|
381
|
+
const raw = document.referrer;
|
|
382
|
+
if (!raw) return void 0;
|
|
383
|
+
const parsed = new URL(raw);
|
|
384
|
+
const referrerHost = parsed.hostname.toLowerCase();
|
|
385
|
+
if (!referrerHost || referrerHost === host) return void 0;
|
|
386
|
+
return referrerHost.slice(0, PAGE_VIEW_LIMITS.maxAttributionValueLength);
|
|
387
|
+
} catch (e) {
|
|
388
|
+
return void 0;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function campaignFromQuery() {
|
|
392
|
+
var _a2;
|
|
393
|
+
try {
|
|
394
|
+
const search = window.location.search;
|
|
395
|
+
if (!search) return void 0;
|
|
396
|
+
const params = new URLSearchParams(search);
|
|
397
|
+
const allow = new Set(
|
|
398
|
+
(_a2 = options.campaignParameters) != null ? _a2 : [
|
|
399
|
+
"utm_source",
|
|
400
|
+
"utm_medium",
|
|
401
|
+
"utm_campaign"
|
|
402
|
+
]
|
|
403
|
+
);
|
|
404
|
+
const bound = (key) => {
|
|
405
|
+
const value = params.get(key);
|
|
406
|
+
if (!value) return void 0;
|
|
407
|
+
return value.slice(0, PAGE_VIEW_LIMITS.maxAttributionValueLength);
|
|
408
|
+
};
|
|
409
|
+
const out = {};
|
|
410
|
+
if (allow.has("utm_source")) out.source = bound("utm_source");
|
|
411
|
+
if (allow.has("utm_medium")) out.medium = bound("utm_medium");
|
|
412
|
+
if (allow.has("utm_campaign")) out.name = bound("utm_campaign");
|
|
413
|
+
if (!out.source && !out.medium && !out.name) return void 0;
|
|
414
|
+
return out;
|
|
415
|
+
} catch (e) {
|
|
416
|
+
return void 0;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function deliver(candidate) {
|
|
420
|
+
const now = Date.now();
|
|
421
|
+
if (client.collectionState !== "granted") {
|
|
422
|
+
return {
|
|
423
|
+
status: "dropped",
|
|
424
|
+
reason: client.collectionState === "pending" ? "consent-pending" : "consent-denied"
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
const sequence = ensureWebSession(now);
|
|
428
|
+
const withSequence = __spreadProps(__spreadValues({}, candidate), { sequence });
|
|
429
|
+
lastCapturedPath = withSequence.path;
|
|
430
|
+
const properties = {
|
|
431
|
+
$page: __spreadValues(__spreadValues({
|
|
432
|
+
host: withSequence.host,
|
|
433
|
+
path: withSequence.path,
|
|
434
|
+
navigation: withSequence.navigation,
|
|
435
|
+
sequence: withSequence.sequence
|
|
436
|
+
}, withSequence.previousPath ? { previousPath: withSequence.previousPath } : {}), withSequence.title ? { title: withSequence.title } : {})
|
|
437
|
+
};
|
|
438
|
+
const referrer = externalReferrerHost(withSequence.host);
|
|
439
|
+
if (referrer) properties.$referrer = { host: referrer };
|
|
440
|
+
const campaign = campaignFromQuery();
|
|
441
|
+
if (campaign) properties.$campaign = campaign;
|
|
442
|
+
const result = seam == null ? void 0 : seam.createReservedEvent(PAGE_VIEW_EVENT_NAME, properties);
|
|
443
|
+
if (!result) {
|
|
444
|
+
return dropped("shutdown");
|
|
445
|
+
}
|
|
446
|
+
if (result.status === "queued") {
|
|
447
|
+
return { status: "queued", eventId: result.eventId };
|
|
448
|
+
}
|
|
449
|
+
if (result.status === "rejected") {
|
|
450
|
+
return { status: "dropped", reason: result.reason };
|
|
451
|
+
}
|
|
452
|
+
return { status: "dropped", reason: result.reason };
|
|
453
|
+
}
|
|
454
|
+
function captureNavigation(navigation, override) {
|
|
455
|
+
if (disposed) {
|
|
456
|
+
return dropped("shutdown");
|
|
457
|
+
}
|
|
458
|
+
const candidate = buildCandidate(navigation, override);
|
|
459
|
+
if (!candidate) {
|
|
460
|
+
return dropped("invalid-page-context");
|
|
461
|
+
}
|
|
462
|
+
if (candidate.path === lastCapturedPath && (navigation === "replace" || navigation === "push" || navigation === "manual")) {
|
|
463
|
+
return dropped("same-path-suppressed");
|
|
464
|
+
}
|
|
465
|
+
return deliver(candidate);
|
|
466
|
+
}
|
|
467
|
+
if (options.mode === "history") {
|
|
468
|
+
removeHistory = acquireHistoryPatch((navigation) => {
|
|
469
|
+
captureNavigation(navigation);
|
|
470
|
+
});
|
|
471
|
+
captureNavigation("initial");
|
|
472
|
+
}
|
|
473
|
+
const controller = {
|
|
474
|
+
mode: options.mode,
|
|
475
|
+
capture(input) {
|
|
476
|
+
var _a2;
|
|
477
|
+
if (disposed) {
|
|
478
|
+
return dropped("shutdown");
|
|
479
|
+
}
|
|
480
|
+
if (options.mode === "history") {
|
|
481
|
+
throw new Error(
|
|
482
|
+
"pageViews.capture() is unavailable in history mode \u2014 navigation is captured automatically"
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
const signature = `${window.location.hostname}|${(_a2 = input == null ? void 0 : input.path) != null ? _a2 : window.location.pathname}`;
|
|
486
|
+
const now = Date.now();
|
|
487
|
+
if (signature === lastManualSignature && now - lastManualAt < 500) {
|
|
488
|
+
lastManualAt = now;
|
|
489
|
+
return dropped("duplicate-manual-capture");
|
|
490
|
+
}
|
|
491
|
+
lastManualSignature = signature;
|
|
492
|
+
lastManualAt = now;
|
|
493
|
+
return captureNavigation("manual", input);
|
|
494
|
+
},
|
|
495
|
+
resetForTests() {
|
|
496
|
+
clearState();
|
|
497
|
+
lastCapturedPath = null;
|
|
498
|
+
lastManualSignature = null;
|
|
499
|
+
},
|
|
500
|
+
dispose() {
|
|
501
|
+
if (disposed) return;
|
|
502
|
+
disposed = true;
|
|
503
|
+
if (removeHistory) {
|
|
504
|
+
removeHistory();
|
|
505
|
+
removeHistory = null;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
return controller;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// src/error-reporter.ts
|
|
513
|
+
import {
|
|
514
|
+
createPrismErrorReporter,
|
|
515
|
+
framesFromStack
|
|
516
|
+
} from "@prism-analytics/core";
|
|
517
|
+
function describeUnknown(value, depth = 0) {
|
|
518
|
+
if (depth > 2) return "[object]";
|
|
519
|
+
if (typeof value === "string") return value;
|
|
520
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null || value === void 0)
|
|
521
|
+
return String(value);
|
|
522
|
+
if (depth === 0 && Array.isArray(value)) return "UnhandledRejection";
|
|
523
|
+
try {
|
|
524
|
+
const seen = /* @__PURE__ */ new Set();
|
|
525
|
+
const text = JSON.stringify(value, (_k, v) => {
|
|
526
|
+
if (typeof v === "object" && v !== null) {
|
|
527
|
+
if (seen.has(v)) return "[Circular]";
|
|
528
|
+
seen.add(v);
|
|
529
|
+
}
|
|
530
|
+
if (typeof v === "function") return "[Function]";
|
|
531
|
+
return v;
|
|
532
|
+
});
|
|
533
|
+
if (text && text.length <= 256) return text;
|
|
534
|
+
return "[object]";
|
|
535
|
+
} catch (e) {
|
|
536
|
+
return "[object]";
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function isPlainRecord(value) {
|
|
540
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
541
|
+
}
|
|
542
|
+
function looksLikeErrorReportInput(value) {
|
|
543
|
+
return isPlainRecord(value) && isPlainRecord(value.exception) && typeof value.exception.type === "string";
|
|
544
|
+
}
|
|
545
|
+
function safeMessage(message, fallback = "Error") {
|
|
546
|
+
const text = typeof message === "string" && message.length > 0 ? message : fallback;
|
|
547
|
+
return text.length > 2048 ? `${text.slice(0, 2048)}\u2026` : text;
|
|
548
|
+
}
|
|
549
|
+
function normalizeErrorValue(value) {
|
|
550
|
+
var _a;
|
|
551
|
+
if (typeof value === "string") {
|
|
552
|
+
return { exception: { type: "Error", message: value } };
|
|
553
|
+
}
|
|
554
|
+
if (value instanceof Error) {
|
|
555
|
+
return {
|
|
556
|
+
exception: {
|
|
557
|
+
type: value.name || "Error",
|
|
558
|
+
message: safeMessage(value.message),
|
|
559
|
+
frames: framesFromStack(value.stack)
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
if (isPlainRecord(value)) {
|
|
564
|
+
const record = value;
|
|
565
|
+
if (record.type === "unhandledrejection") {
|
|
566
|
+
return normalizeErrorValue((_a = record.reason) != null ? _a : "Unhandled rejection");
|
|
567
|
+
}
|
|
568
|
+
if (looksLikeErrorReportInput(record)) {
|
|
569
|
+
return record;
|
|
570
|
+
}
|
|
571
|
+
const name = record.name;
|
|
572
|
+
const message = record.message;
|
|
573
|
+
if (typeof name === "string" && name.length > 0 && typeof message === "string") {
|
|
574
|
+
return {
|
|
575
|
+
exception: {
|
|
576
|
+
type: name,
|
|
577
|
+
message: safeMessage(message),
|
|
578
|
+
frames: framesFromStack(
|
|
579
|
+
typeof record.stack === "string" ? record.stack : void 0
|
|
580
|
+
)
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
if (typeof message === "string" && message.length > 0) {
|
|
585
|
+
return __spreadValues({
|
|
586
|
+
exception: {
|
|
587
|
+
type: message === "Script error." ? "ScriptError" : "Error",
|
|
588
|
+
message: safeMessage(message)
|
|
589
|
+
}
|
|
590
|
+
}, message === "Script error." ? { context: { extras: { scriptError: true } } } : {});
|
|
591
|
+
}
|
|
592
|
+
return {
|
|
593
|
+
exception: {
|
|
594
|
+
type: "UnhandledRejection",
|
|
595
|
+
message: describeUnknown(record)
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
if (typeof value === "object" && value !== null) {
|
|
600
|
+
return {
|
|
601
|
+
exception: { type: "Error", message: describeUnknown(value) }
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
return {
|
|
605
|
+
exception: { type: "Error", message: describeUnknown(value) }
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
var DEDUPE_DEFAULT_MS = 1e3;
|
|
609
|
+
var DEDUPE_MAX_ENTRIES = 100;
|
|
610
|
+
function createBrowserErrorReporter(options) {
|
|
611
|
+
return __async(this, null, function* () {
|
|
612
|
+
var _a;
|
|
613
|
+
if (typeof window === "undefined") {
|
|
614
|
+
throw new Error(
|
|
615
|
+
"@prism-analytics/browser error reporter requires a browser (window is undefined)"
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
if (!options.endpoint || options.endpoint.trim().length === 0) {
|
|
619
|
+
throw new Error(
|
|
620
|
+
"endpoint is required \u2014 choose the ingestion origin at runtime"
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
if (!options.share || typeof options.share.consent !== "function") {
|
|
624
|
+
throw new Error("share is required \u2014 wire the analytics client's consent");
|
|
625
|
+
}
|
|
626
|
+
const runtime = createBrowserRuntime();
|
|
627
|
+
const reporter = yield createPrismErrorReporter(__spreadValues({
|
|
628
|
+
sourceKey: options.sourceKey,
|
|
629
|
+
endpoint: options.endpoint,
|
|
630
|
+
runtime,
|
|
631
|
+
share: options.share,
|
|
632
|
+
beforeSend: options.beforeSend,
|
|
633
|
+
queue: options.queue
|
|
634
|
+
}, "onDiagnostic" in options && options.onDiagnostic ? { onDiagnostic: options.onDiagnostic } : {}));
|
|
635
|
+
const dedupeMs = (_a = options.dedupeMs) != null ? _a : DEDUPE_DEFAULT_MS;
|
|
636
|
+
const seen = /* @__PURE__ */ new Map();
|
|
637
|
+
let installed = false;
|
|
638
|
+
let handleError = null;
|
|
639
|
+
let handleRejection = null;
|
|
640
|
+
const capture = (value, captureOptions) => {
|
|
641
|
+
var _a2, _b, _c, _d, _e;
|
|
642
|
+
let input;
|
|
643
|
+
try {
|
|
644
|
+
input = normalizeErrorValue(value);
|
|
645
|
+
} catch (error) {
|
|
646
|
+
throw error instanceof Error ? error : new Error("invalid error report");
|
|
647
|
+
}
|
|
648
|
+
const release = (_b = (_a2 = captureOptions == null ? void 0 : captureOptions.release) != null ? _a2 : options.release) != null ? _b : input.release;
|
|
649
|
+
const environment = (_d = (_c = captureOptions == null ? void 0 : captureOptions.environment) != null ? _c : options.environment) != null ? _d : input.environment;
|
|
650
|
+
input = __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, input), release ? { release } : {}), environment ? { environment } : {}), (captureOptions == null ? void 0 : captureOptions.level) ? { level: captureOptions.level } : {}), (captureOptions == null ? void 0 : captureOptions.context) ? { context: captureOptions.context } : {}), (captureOptions == null ? void 0 : captureOptions.breadcrumbs) ? { breadcrumbs: captureOptions.breadcrumbs } : {}), "handled" in (captureOptions != null ? captureOptions : {}) ? { handled: captureOptions == null ? void 0 : captureOptions.handled } : { handled: true });
|
|
651
|
+
const type = input.exception.type;
|
|
652
|
+
const key = `${type}\0${(_e = input.exception.message) != null ? _e : ""}`;
|
|
653
|
+
const now = runtime.now();
|
|
654
|
+
const prior = seen.get(key);
|
|
655
|
+
if (prior && now - prior.ts < dedupeMs) {
|
|
656
|
+
return { status: "deduped", id: prior.id };
|
|
657
|
+
}
|
|
658
|
+
if (seen.size >= DEDUPE_MAX_ENTRIES) {
|
|
659
|
+
for (const k of seen.keys()) {
|
|
660
|
+
seen.delete(k);
|
|
661
|
+
if (seen.size < DEDUPE_MAX_ENTRIES) break;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
const result = reporter.captureException(input);
|
|
665
|
+
if (result.status === "queued") {
|
|
666
|
+
seen.set(key, { ts: now, id: result.id });
|
|
667
|
+
}
|
|
668
|
+
return result;
|
|
669
|
+
};
|
|
670
|
+
const install = () => {
|
|
671
|
+
if (installed) return;
|
|
672
|
+
installed = true;
|
|
673
|
+
handleError = (event) => {
|
|
674
|
+
var _a2, _b;
|
|
675
|
+
const errorEvent = event;
|
|
676
|
+
const message = errorEvent.message;
|
|
677
|
+
const error = errorEvent.error;
|
|
678
|
+
try {
|
|
679
|
+
if (error && error instanceof Error) {
|
|
680
|
+
capture(error, { handled: false });
|
|
681
|
+
} else if (typeof message === "string" && message.length > 0) {
|
|
682
|
+
capture(
|
|
683
|
+
{
|
|
684
|
+
exception: { type: "ScriptError", message },
|
|
685
|
+
context: { extras: { scriptError: message === "Script error." } }
|
|
686
|
+
},
|
|
687
|
+
{ handled: false }
|
|
688
|
+
);
|
|
689
|
+
} else {
|
|
690
|
+
const target = errorEvent.target;
|
|
691
|
+
capture(
|
|
692
|
+
{
|
|
693
|
+
exception: {
|
|
694
|
+
type: "ResourceError",
|
|
695
|
+
message: (_b = (_a2 = target == null ? void 0 : target.src) != null ? _a2 : target == null ? void 0 : target.tagName) != null ? _b : "Resource load failed"
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
{ handled: false }
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
} catch (e) {
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
handleRejection = (event) => {
|
|
705
|
+
const reason = event.reason;
|
|
706
|
+
try {
|
|
707
|
+
capture(reason != null ? reason : "Unhandled rejection", { handled: false });
|
|
708
|
+
} catch (e) {
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
window.addEventListener("error", handleError, true);
|
|
712
|
+
window.addEventListener("unhandledrejection", handleRejection);
|
|
713
|
+
};
|
|
714
|
+
const uninstall = () => {
|
|
715
|
+
if (!installed) return;
|
|
716
|
+
installed = false;
|
|
717
|
+
if (handleError) window.removeEventListener("error", handleError, true);
|
|
718
|
+
if (handleRejection)
|
|
719
|
+
window.removeEventListener("unhandledrejection", handleRejection);
|
|
720
|
+
handleError = null;
|
|
721
|
+
handleRejection = null;
|
|
722
|
+
};
|
|
723
|
+
const bound = {
|
|
724
|
+
reporter,
|
|
725
|
+
get pendingCount() {
|
|
726
|
+
return reporter.pendingCount;
|
|
727
|
+
},
|
|
728
|
+
get installed() {
|
|
729
|
+
return installed;
|
|
730
|
+
},
|
|
731
|
+
captureException: capture,
|
|
732
|
+
install,
|
|
733
|
+
uninstall,
|
|
734
|
+
flush: () => reporter.flush(),
|
|
735
|
+
shutdown: (opts) => __async(null, null, function* () {
|
|
736
|
+
uninstall();
|
|
737
|
+
yield reporter.shutdown(opts);
|
|
738
|
+
}),
|
|
739
|
+
onDiagnostic: (listener) => reporter.onDiagnostic(listener)
|
|
740
|
+
};
|
|
741
|
+
if (options.captureGlobalErrors) install();
|
|
742
|
+
return bound;
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// src/index.ts
|
|
747
|
+
import { framesFromStack as framesFromStack2 } from "@prism-analytics/core";
|
|
748
|
+
function createBrowserClient(options) {
|
|
749
|
+
return __async(this, null, function* () {
|
|
750
|
+
if (typeof window === "undefined") {
|
|
751
|
+
throw new Error(
|
|
752
|
+
"@prism-analytics/browser requires a browser environment (window is undefined)"
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
if (!options.endpoint || options.endpoint.trim().length === 0) {
|
|
756
|
+
throw new Error(
|
|
757
|
+
"endpoint is required \u2014 choose the ingestion origin at runtime"
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
const runtime = createBrowserRuntime();
|
|
761
|
+
const client = yield createPrismClient({
|
|
762
|
+
sourceKey: options.sourceKey,
|
|
763
|
+
endpoint: options.endpoint.replace(/\/$/, ""),
|
|
764
|
+
runtime,
|
|
765
|
+
collection: __spreadValues({
|
|
766
|
+
// Session-scoped identity by default (§4): the browser default
|
|
767
|
+
// keeps the anonymous ID for the client lifetime unless the caller
|
|
768
|
+
// explicitly asks for persistent identity.
|
|
769
|
+
anonymousPersistence: "session"
|
|
770
|
+
}, options.collection),
|
|
771
|
+
queue: options.queue,
|
|
772
|
+
sanitize: options.sanitize
|
|
773
|
+
});
|
|
774
|
+
const browserClient = client;
|
|
775
|
+
if (!options.pageViews) {
|
|
776
|
+
browserClient.pageViews = null;
|
|
777
|
+
return browserClient;
|
|
778
|
+
}
|
|
779
|
+
const coreClient = client;
|
|
780
|
+
const tracker = createPageTracker({
|
|
781
|
+
client: coreClient,
|
|
782
|
+
options: options.pageViews,
|
|
783
|
+
sourceKey: options.sourceKey,
|
|
784
|
+
endpoint: options.endpoint.replace(/\/$/, "")
|
|
785
|
+
});
|
|
786
|
+
browserClient.pageViews = {
|
|
787
|
+
mode: tracker.mode,
|
|
788
|
+
capture: (input) => tracker.capture(input)
|
|
789
|
+
};
|
|
790
|
+
const originalShutdown = client.shutdown.bind(client);
|
|
791
|
+
browserClient.shutdown = (shutdownOptions) => __async(null, null, function* () {
|
|
792
|
+
tracker.dispose();
|
|
793
|
+
return originalShutdown(shutdownOptions);
|
|
794
|
+
});
|
|
795
|
+
return browserClient;
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
export {
|
|
799
|
+
capturePageContext,
|
|
800
|
+
createBrowserClient,
|
|
801
|
+
createBrowserErrorReporter,
|
|
802
|
+
framesFromStack2 as framesFromStack,
|
|
803
|
+
normalizeErrorValue
|
|
804
|
+
};
|