495 lines
15 KiB
JavaScript
495 lines
15 KiB
JavaScript
(() => {
|
|
var y = Object.create;
|
|
var c = Object.defineProperty;
|
|
var g = Object.getOwnPropertyDescriptor;
|
|
var v = Object.getOwnPropertyNames;
|
|
var m = Object.getPrototypeOf,
|
|
k = Object.prototype.hasOwnProperty;
|
|
var w = (e, s) => () => (s || e((s = { exports: {} }).exports, s), s.exports);
|
|
var b = (e, s, t, i) => {
|
|
if ((s && typeof s == 'object') || typeof s == 'function') {
|
|
for (const n of v(s)) {
|
|
!k.call(e, n) &&
|
|
n !== t &&
|
|
c(e, n, {
|
|
get: () => s[n],
|
|
enumerable: !(i = g(s, n)) || i.enumerable,
|
|
});
|
|
}
|
|
}
|
|
return e;
|
|
};
|
|
var P = (e, s, t) => (
|
|
(t = e != null ? y(m(e)) : {}),
|
|
b(
|
|
s || !e || !e.__esModule
|
|
? c(t, 'default', { value: e, enumerable: !0 })
|
|
: t,
|
|
e
|
|
)
|
|
);
|
|
var f = w((x, h) => {
|
|
h.exports = {};
|
|
});
|
|
var I = class {
|
|
constructor(e) {
|
|
(this.baseUrl = e.baseUrl),
|
|
(this.headers = {
|
|
'Content-Type': 'application/json',
|
|
...e.defaultHeaders,
|
|
}),
|
|
(this.maxRetries = e.maxRetries ?? 3),
|
|
(this.initialRetryDelay = e.initialRetryDelay ?? 500);
|
|
}
|
|
async resolveHeaders() {
|
|
const e = {};
|
|
for (const [s, t] of Object.entries(this.headers)) {
|
|
const i = await t;
|
|
i !== null && (e[s] = i);
|
|
}
|
|
return e;
|
|
}
|
|
addHeader(e, s) {
|
|
this.headers[e] = s;
|
|
}
|
|
async post(e, s, t, i) {
|
|
try {
|
|
const n = await fetch(e, {
|
|
method: 'POST',
|
|
headers: await this.resolveHeaders(),
|
|
body: s ? JSON.stringify(s ?? {}) : void 0,
|
|
keepalive: !0,
|
|
...t,
|
|
});
|
|
if (n.status === 401) {
|
|
return null;
|
|
}
|
|
if (n.status !== 200 && n.status !== 202) {
|
|
throw new Error(`HTTP error! status: ${n.status}`);
|
|
}
|
|
const r = await n.text();
|
|
return r ? JSON.parse(r) : null;
|
|
} catch (n) {
|
|
if (i < this.maxRetries) {
|
|
const r = this.initialRetryDelay * 2 ** i;
|
|
return (
|
|
await new Promise((a) => setTimeout(a, r)),
|
|
this.post(e, s, t, i + 1)
|
|
);
|
|
}
|
|
return console.error('Max retries reached:', n), null;
|
|
}
|
|
}
|
|
async fetch(e, s, t = {}) {
|
|
const i = `${this.baseUrl}${e}`;
|
|
return this.post(i, s, t, 0);
|
|
}
|
|
},
|
|
d = class {
|
|
constructor(e) {
|
|
(this.groups = []), (this.queue = []), (this.options = e);
|
|
const s = { 'openpanel-client-id': e.clientId };
|
|
e.clientSecret && (s['openpanel-client-secret'] = e.clientSecret),
|
|
(s['openpanel-sdk-name'] = e.sdk || 'node'),
|
|
(s['openpanel-sdk-version'] = e.sdkVersion || '1.3.0'),
|
|
(this.api = new I({
|
|
baseUrl: e.apiUrl || 'https://api.openpanel.dev',
|
|
defaultHeaders: s,
|
|
}));
|
|
}
|
|
init() {}
|
|
ready() {
|
|
(this.options.disabled = !1),
|
|
(this.options.waitForProfile = !1),
|
|
this.flush();
|
|
}
|
|
shouldQueue(e) {
|
|
return !!(
|
|
this.options.disabled ||
|
|
(this.options.waitForProfile && !this.profileId) ||
|
|
(e.type === 'replay' && !this.sessionId)
|
|
);
|
|
}
|
|
addQueue(e) {
|
|
e.type === 'track' &&
|
|
(e.payload.properties = {
|
|
...(e.payload.properties ?? {}),
|
|
__timestamp: new Date().toISOString(),
|
|
}),
|
|
this.queue.push(e);
|
|
}
|
|
async send(e) {
|
|
if (this.options.filter && !this.options.filter(e)) {
|
|
return Promise.resolve();
|
|
}
|
|
if (this.shouldQueue(e)) {
|
|
return this.addQueue(e), Promise.resolve();
|
|
}
|
|
const s = await this.api.fetch('/track', e, {
|
|
keepalive: e.type !== 'replay',
|
|
});
|
|
this.deviceId = s?.deviceId;
|
|
const t = !!this.sessionId;
|
|
return (
|
|
(this.sessionId = s?.sessionId),
|
|
!t && this.sessionId && this.flush(),
|
|
s
|
|
);
|
|
}
|
|
setGlobalProperties(e) {
|
|
this.global = { ...this.global, ...e };
|
|
}
|
|
track(e, s) {
|
|
this.log('track event', e, s);
|
|
const { groups: t, profileId: i, ...n } = s ?? {},
|
|
r = [...new Set([...this.groups, ...(t ?? [])])];
|
|
return this.send({
|
|
type: 'track',
|
|
payload: {
|
|
name: e,
|
|
profileId: i ?? this.profileId,
|
|
groups: r.length > 0 ? r : void 0,
|
|
properties: { ...(this.global ?? {}), ...n },
|
|
},
|
|
});
|
|
}
|
|
identify(e) {
|
|
if (
|
|
(this.log('identify user', e),
|
|
e.profileId && ((this.profileId = e.profileId), this.flush()),
|
|
Object.keys(e).length > 1)
|
|
) {
|
|
return this.send({
|
|
type: 'identify',
|
|
payload: { ...e, properties: { ...this.global, ...e.properties } },
|
|
});
|
|
}
|
|
}
|
|
upsertGroup(e) {
|
|
return (
|
|
this.log('upsert group', e), this.send({ type: 'group', payload: e })
|
|
);
|
|
}
|
|
setGroup(e) {
|
|
return (
|
|
this.log('set group', e),
|
|
this.groups.includes(e) || (this.groups = [...this.groups, e]),
|
|
this.send({
|
|
type: 'assign_group',
|
|
payload: { groupIds: [e], profileId: this.profileId },
|
|
})
|
|
);
|
|
}
|
|
setGroups(e) {
|
|
return (
|
|
this.log('set groups', e),
|
|
(this.groups = [...new Set([...this.groups, ...e])]),
|
|
this.send({
|
|
type: 'assign_group',
|
|
payload: { groupIds: e, profileId: this.profileId },
|
|
})
|
|
);
|
|
}
|
|
alias(e) {}
|
|
increment(e) {
|
|
return this.send({ type: 'increment', payload: e });
|
|
}
|
|
decrement(e) {
|
|
return this.send({ type: 'decrement', payload: e });
|
|
}
|
|
revenue(e, s) {
|
|
const t = s?.deviceId;
|
|
return (
|
|
delete s?.deviceId,
|
|
this.track('revenue', {
|
|
...(s ?? {}),
|
|
...(t ? { __deviceId: t } : {}),
|
|
__revenue: e,
|
|
})
|
|
);
|
|
}
|
|
getDeviceId() {
|
|
return this.deviceId ?? '';
|
|
}
|
|
getSessionId() {
|
|
return this.sessionId ?? '';
|
|
}
|
|
fetchDeviceId() {
|
|
return Promise.resolve(this.deviceId ?? '');
|
|
}
|
|
clear() {
|
|
(this.profileId = void 0),
|
|
(this.groups = []),
|
|
(this.deviceId = void 0),
|
|
(this.sessionId = void 0);
|
|
}
|
|
buildFlushPayload(e) {
|
|
if (e.type === 'replay') {
|
|
return e.payload;
|
|
}
|
|
if (e.type === 'track') {
|
|
const s = 'groups' in e.payload ? (e.payload.groups ?? []) : [],
|
|
t = [...new Set([...this.groups, ...s])];
|
|
return {
|
|
...e.payload,
|
|
profileId: e.payload.profileId ?? this.profileId,
|
|
groups: t.length > 0 ? t : void 0,
|
|
};
|
|
}
|
|
return e.type === 'identify' ||
|
|
e.type === 'increment' ||
|
|
e.type === 'decrement'
|
|
? { ...e.payload, profileId: e.payload.profileId ?? this.profileId }
|
|
: e.type === 'assign_group'
|
|
? { ...e.payload, profileId: e.payload.profileId ?? this.profileId }
|
|
: e.payload;
|
|
}
|
|
flush() {
|
|
const e = [];
|
|
for (const s of this.queue) {
|
|
if (this.shouldQueue(s)) {
|
|
e.push(s);
|
|
continue;
|
|
}
|
|
const t = this.buildFlushPayload(s);
|
|
this.send({ ...s, payload: t });
|
|
}
|
|
this.queue = e;
|
|
}
|
|
log(...e) {
|
|
this.options.debug && console.log('[OpenPanel.dev]', ...e);
|
|
}
|
|
};
|
|
var S = typeof document < 'u' ? document.currentScript : null;
|
|
function R(e) {
|
|
return e.replace(/([-_][a-z])/gi, (s) =>
|
|
s.toUpperCase().replace('-', '').replace('_', '')
|
|
);
|
|
}
|
|
var l = class extends d {
|
|
constructor(t) {
|
|
super({ sdk: 'web', sdkVersion: '1.3.0', ...t });
|
|
this.options = t;
|
|
this.lastPath = '';
|
|
this.pendingRevenues = [];
|
|
if (!this.isServer()) {
|
|
try {
|
|
const i = sessionStorage.getItem('openpanel-pending-revenues');
|
|
if (i) {
|
|
const n = JSON.parse(i);
|
|
Array.isArray(n) && (this.pendingRevenues = n);
|
|
}
|
|
} catch {
|
|
this.pendingRevenues = [];
|
|
}
|
|
if (
|
|
(this.setGlobalProperties({ __referrer: document.referrer }),
|
|
this.options.trackScreenViews &&
|
|
(this.trackScreenViews(), setTimeout(() => this.screenView(), 0)),
|
|
this.options.trackOutgoingLinks && this.trackOutgoingLinks(),
|
|
this.options.trackAttributes && this.trackAttributes(),
|
|
this.options.sessionReplay?.enabled)
|
|
) {
|
|
const i = this.options.sessionReplay.sampleRate ?? 1;
|
|
Math.random() < i &&
|
|
this.loadReplayModule().then((r) => {
|
|
r &&
|
|
r.startReplayRecorder(this.options.sessionReplay, (a) => {
|
|
this.send({ type: 'replay', payload: { ...a } });
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
async loadReplayModule() {
|
|
try {
|
|
{
|
|
const t = S,
|
|
i =
|
|
this.options.sessionReplay?.scriptUrl ||
|
|
t?.src?.replace('.js', '-replay.js') ||
|
|
'https://openpanel.dev/op1-replay.js';
|
|
return window.__openpanel_replay
|
|
? window.__openpanel_replay
|
|
: new Promise((n) => {
|
|
const r = document.createElement('script');
|
|
(r.src = i),
|
|
(r.onload = () => {
|
|
n(window.__openpanel_replay ?? null);
|
|
}),
|
|
(r.onerror = () => {
|
|
console.warn(
|
|
'[OpenPanel] Failed to load replay script from',
|
|
i
|
|
),
|
|
n(null);
|
|
}),
|
|
document.head.appendChild(r);
|
|
});
|
|
}
|
|
return await Promise.resolve().then(() => P(f(), 1));
|
|
} catch (t) {
|
|
return (
|
|
console.warn('[OpenPanel] Failed to load replay module', t), null
|
|
);
|
|
}
|
|
}
|
|
debounce(t, i) {
|
|
clearTimeout(this.debounceTimer), (this.debounceTimer = setTimeout(t, i));
|
|
}
|
|
isServer() {
|
|
return typeof document > 'u';
|
|
}
|
|
trackOutgoingLinks() {
|
|
this.isServer() ||
|
|
document.addEventListener('click', (t) => {
|
|
const i = t.target,
|
|
n = i.closest('a');
|
|
if (n && i) {
|
|
const r = n.getAttribute('href');
|
|
if (r?.startsWith('http')) {
|
|
try {
|
|
const a = new URL(r),
|
|
o = window.location.hostname;
|
|
a.hostname !== o &&
|
|
super.track('link_out', {
|
|
href: r,
|
|
text:
|
|
n.innerText ||
|
|
n.getAttribute('title') ||
|
|
i.getAttribute('alt') ||
|
|
i.getAttribute('title'),
|
|
});
|
|
} catch {}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
trackScreenViews() {
|
|
if (this.isServer()) {
|
|
return;
|
|
}
|
|
const t = history.pushState;
|
|
history.pushState = function (...a) {
|
|
const o = t.apply(this, a);
|
|
return (
|
|
window.dispatchEvent(new Event('pushstate')),
|
|
window.dispatchEvent(new Event('locationchange')),
|
|
o
|
|
);
|
|
};
|
|
const i = history.replaceState;
|
|
(history.replaceState = function (...a) {
|
|
const o = i.apply(this, a);
|
|
return (
|
|
window.dispatchEvent(new Event('replacestate')),
|
|
window.dispatchEvent(new Event('locationchange')),
|
|
o
|
|
);
|
|
}),
|
|
window.addEventListener('popstate', () => {
|
|
window.dispatchEvent(new Event('locationchange'));
|
|
});
|
|
const n = () => this.debounce(() => this.screenView(), 50);
|
|
this.options.trackHashChanges
|
|
? window.addEventListener('hashchange', n)
|
|
: window.addEventListener('locationchange', n);
|
|
}
|
|
trackAttributes() {
|
|
this.isServer() ||
|
|
document.addEventListener('click', (t) => {
|
|
const i = t.target,
|
|
n = i.closest('button'),
|
|
r = i.closest('a'),
|
|
a = n?.getAttribute('data-track')
|
|
? n
|
|
: r?.getAttribute('data-track')
|
|
? r
|
|
: null;
|
|
if (a) {
|
|
const o = {};
|
|
for (const p of a.attributes) {
|
|
p.name.startsWith('data-') &&
|
|
p.name !== 'data-track' &&
|
|
(o[R(p.name.replace(/^data-/, ''))] = p.value);
|
|
}
|
|
const u = a.getAttribute('data-track');
|
|
u && super.track(u, o);
|
|
}
|
|
});
|
|
}
|
|
track(t, i) {
|
|
return super.track(t, { ...i, __path: this.lastPath });
|
|
}
|
|
screenView(t, i) {
|
|
if (this.isServer()) {
|
|
return;
|
|
}
|
|
let n, r;
|
|
typeof t == 'string'
|
|
? ((n = t), (r = i))
|
|
: ((n = window.location.href), (r = t)),
|
|
this.lastPath !== n &&
|
|
((this.lastPath = n),
|
|
super.track('screen_view', {
|
|
...(r ?? {}),
|
|
__path: n,
|
|
__title: document.title,
|
|
}));
|
|
}
|
|
async flushRevenue() {
|
|
const t = this.pendingRevenues.map((i) =>
|
|
super.revenue(i.amount, i.properties)
|
|
);
|
|
await Promise.all(t), this.clearRevenue();
|
|
}
|
|
clearRevenue() {
|
|
if (((this.pendingRevenues = []), !this.isServer())) {
|
|
try {
|
|
sessionStorage.removeItem('openpanel-pending-revenues');
|
|
} catch {}
|
|
}
|
|
}
|
|
pendingRevenue(t, i) {
|
|
if (
|
|
(this.pendingRevenues.push({ amount: t, properties: i }),
|
|
!this.isServer())
|
|
) {
|
|
try {
|
|
sessionStorage.setItem(
|
|
'openpanel-pending-revenues',
|
|
JSON.stringify(this.pendingRevenues)
|
|
);
|
|
} catch {}
|
|
}
|
|
}
|
|
};
|
|
((e) => {
|
|
if (e.op) {
|
|
const s = e.op.q || [],
|
|
t = new l(s.shift()[1]);
|
|
s.forEach((n) => {
|
|
n[0] in t && t[n[0]](...n.slice(1));
|
|
});
|
|
const i = new Proxy(
|
|
(n, ...r) => {
|
|
const a = t[n] ? t[n].bind(t) : void 0;
|
|
typeof a == 'function'
|
|
? a(...r)
|
|
: console.warn(`OpenPanel: ${n} is not a function`);
|
|
},
|
|
{
|
|
get(n, r) {
|
|
if (r === 'q') {
|
|
return;
|
|
}
|
|
const a = t[r];
|
|
return typeof a == 'function' ? a.bind(t) : a;
|
|
},
|
|
}
|
|
);
|
|
(e.op = i), (e.openpanel = t);
|
|
}
|
|
})(window);
|
|
})();
|