@clickroom/sdk-js 0.2.0
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 +106 -0
- package/dist/__tests__/channel.test.d.ts +1 -0
- package/dist/__tests__/device.test.d.ts +1 -0
- package/dist/__tests__/exposure.test.d.ts +1 -0
- package/dist/__tests__/flag-polling.test.d.ts +1 -0
- package/dist/__tests__/flags.test.d.ts +1 -0
- package/dist/__tests__/identify.test.d.ts +1 -0
- package/dist/__tests__/session.test.d.ts +1 -0
- package/dist/__tests__/transport.test.d.ts +1 -0
- package/dist/__tests__/utm.test.d.ts +1 -0
- package/dist/autocapture.d.ts +7 -0
- package/dist/channel.d.ts +11 -0
- package/dist/clickroom.js +2 -0
- package/dist/clickroom.js.map +7 -0
- package/dist/device.d.ts +5 -0
- package/dist/elements-chain.d.ts +13 -0
- package/dist/eval.d.ts +3 -0
- package/dist/eval.js +151 -0
- package/dist/eval.js.map +7 -0
- package/dist/flags.d.ts +74 -0
- package/dist/global.d.ts +7 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +1004 -0
- package/dist/index.js.map +7 -0
- package/dist/loader.min.js +2 -0
- package/dist/loader.min.js.map +7 -0
- package/dist/session.d.ts +15 -0
- package/dist/sha1.d.ts +2 -0
- package/dist/transport.d.ts +7 -0
- package/dist/types.d.ts +22 -0
- package/dist/utm.d.ts +5 -0
- package/loader.js +72 -0
- package/package.json +59 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
// src/elements-chain.ts
|
|
2
|
+
var MAX_DEPTH = 15;
|
|
3
|
+
var MAX_TEXT_LEN = 255;
|
|
4
|
+
var INTERESTING_TAGS = /* @__PURE__ */ new Set(["a", "button", "input", "select", "textarea", "label"]);
|
|
5
|
+
var OPT_OUT_CLASS = "ch-no-capture";
|
|
6
|
+
var OPT_OUT_ATTR = "data-ch-no-capture";
|
|
7
|
+
function escapeValue(value) {
|
|
8
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/;/g, "\\;").replace(/\|/g, "\\|").replace(/\r?\n/g, " ").trim();
|
|
9
|
+
}
|
|
10
|
+
function nthChild(el) {
|
|
11
|
+
let n = 1;
|
|
12
|
+
let sib = el.previousElementSibling;
|
|
13
|
+
while (sib) {
|
|
14
|
+
n += 1;
|
|
15
|
+
sib = sib.previousElementSibling;
|
|
16
|
+
}
|
|
17
|
+
return n;
|
|
18
|
+
}
|
|
19
|
+
function nthOfType(el) {
|
|
20
|
+
let n = 1;
|
|
21
|
+
const tag = el.tagName;
|
|
22
|
+
let sib = el.previousElementSibling;
|
|
23
|
+
while (sib) {
|
|
24
|
+
if (sib.tagName === tag) {
|
|
25
|
+
n += 1;
|
|
26
|
+
}
|
|
27
|
+
sib = sib.previousElementSibling;
|
|
28
|
+
}
|
|
29
|
+
return n;
|
|
30
|
+
}
|
|
31
|
+
function elementText(el) {
|
|
32
|
+
const raw = el.innerText ?? el.textContent ?? "";
|
|
33
|
+
const trimmed = raw.replace(/\s+/g, " ").trim();
|
|
34
|
+
return trimmed.length > MAX_TEXT_LEN ? trimmed.slice(0, MAX_TEXT_LEN) : trimmed;
|
|
35
|
+
}
|
|
36
|
+
function serializeElement(el) {
|
|
37
|
+
const tag = el.tagName.toLowerCase();
|
|
38
|
+
const pairs = [];
|
|
39
|
+
const id = el.getAttribute("id");
|
|
40
|
+
if (id) {
|
|
41
|
+
pairs.push(`attr__id="${escapeValue(id)}"`);
|
|
42
|
+
}
|
|
43
|
+
const className = typeof el.className === "string" ? el.className : "";
|
|
44
|
+
if (className) {
|
|
45
|
+
pairs.push(`attr__class="${escapeValue(className)}"`);
|
|
46
|
+
}
|
|
47
|
+
if (tag === "a") {
|
|
48
|
+
const href = el.getAttribute("href");
|
|
49
|
+
if (href) {
|
|
50
|
+
pairs.push(`href="${escapeValue(href)}"`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const text = elementText(el);
|
|
54
|
+
if (text) {
|
|
55
|
+
pairs.push(`text="${escapeValue(text)}"`);
|
|
56
|
+
}
|
|
57
|
+
pairs.push(`nth-child="${nthChild(el)}"`);
|
|
58
|
+
pairs.push(`nth-of-type="${nthOfType(el)}"`);
|
|
59
|
+
const attrs = el.attributes;
|
|
60
|
+
for (let i = 0; i < attrs.length; i += 1) {
|
|
61
|
+
const attr = attrs.item(i);
|
|
62
|
+
if (attr?.name.startsWith("data-")) {
|
|
63
|
+
pairs.push(`attr__${attr.name}="${escapeValue(attr.value)}"`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return `${tag}:${pairs.join(";")}`;
|
|
67
|
+
}
|
|
68
|
+
function serializeElementsChain(target) {
|
|
69
|
+
const parts = [];
|
|
70
|
+
let el = target;
|
|
71
|
+
let depth = 0;
|
|
72
|
+
while (el && depth < MAX_DEPTH) {
|
|
73
|
+
const tag = el.tagName.toLowerCase();
|
|
74
|
+
if (tag === "body" || tag === "html") {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
parts.push(serializeElement(el));
|
|
78
|
+
el = el.parentElement;
|
|
79
|
+
depth += 1;
|
|
80
|
+
}
|
|
81
|
+
return parts.join("|");
|
|
82
|
+
}
|
|
83
|
+
function isOptedOut(el) {
|
|
84
|
+
let node = el;
|
|
85
|
+
while (node) {
|
|
86
|
+
if (node.classList?.contains(OPT_OUT_CLASS) || node.hasAttribute(OPT_OUT_ATTR)) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
node = node.parentElement;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
function isInteresting(el) {
|
|
94
|
+
const tag = el.tagName.toLowerCase();
|
|
95
|
+
if (INTERESTING_TAGS.has(tag)) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (el.hasAttribute("data-ch-capture")) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (el.getAttribute("role")) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
if (el.hasAttribute("onclick")) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
function getEventTarget(el) {
|
|
110
|
+
let node = el;
|
|
111
|
+
let depth = 0;
|
|
112
|
+
while (node && depth < MAX_DEPTH) {
|
|
113
|
+
if (isOptedOut(node)) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
if (isInteresting(node)) {
|
|
117
|
+
return node;
|
|
118
|
+
}
|
|
119
|
+
node = node.parentElement;
|
|
120
|
+
depth += 1;
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/autocapture.ts
|
|
126
|
+
function locationProps() {
|
|
127
|
+
return {
|
|
128
|
+
$current_url: location.href,
|
|
129
|
+
$host: location.host,
|
|
130
|
+
$pathname: location.pathname
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function initAutocapture(ctx) {
|
|
134
|
+
if (typeof document === "undefined" || typeof window === "undefined") {
|
|
135
|
+
return () => {
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const handleInteraction = (event) => {
|
|
139
|
+
try {
|
|
140
|
+
const raw = event.target;
|
|
141
|
+
if (!(raw instanceof Element)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const target = getEventTarget(raw);
|
|
145
|
+
if (!target) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const chain = serializeElementsChain(target);
|
|
149
|
+
ctx.capture(
|
|
150
|
+
"$autocapture",
|
|
151
|
+
{
|
|
152
|
+
$event_type: event.type,
|
|
153
|
+
...locationProps()
|
|
154
|
+
},
|
|
155
|
+
chain
|
|
156
|
+
);
|
|
157
|
+
} catch {
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const interactionEvents = ["click", "submit", "change"];
|
|
161
|
+
if (ctx.config.autocapture) {
|
|
162
|
+
for (const type of interactionEvents) {
|
|
163
|
+
document.addEventListener(type, handleInteraction, true);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
let lastUrl = "";
|
|
167
|
+
const emitPageview = () => {
|
|
168
|
+
try {
|
|
169
|
+
if (!ctx.config.capture_pageview) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (location.href === lastUrl) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
lastUrl = location.href;
|
|
176
|
+
ctx.capture("$pageview", {
|
|
177
|
+
...locationProps(),
|
|
178
|
+
$referrer: document.referrer
|
|
179
|
+
});
|
|
180
|
+
} catch {
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const origPushState = history.pushState.bind(history);
|
|
184
|
+
const origReplaceState = history.replaceState.bind(history);
|
|
185
|
+
let patched = false;
|
|
186
|
+
if (ctx.config.capture_pageview) {
|
|
187
|
+
history.pushState = (...args) => {
|
|
188
|
+
origPushState(...args);
|
|
189
|
+
emitPageview();
|
|
190
|
+
};
|
|
191
|
+
history.replaceState = (...args) => {
|
|
192
|
+
origReplaceState(...args);
|
|
193
|
+
emitPageview();
|
|
194
|
+
};
|
|
195
|
+
patched = true;
|
|
196
|
+
window.addEventListener("popstate", emitPageview);
|
|
197
|
+
window.addEventListener("hashchange", emitPageview);
|
|
198
|
+
emitPageview();
|
|
199
|
+
}
|
|
200
|
+
return () => {
|
|
201
|
+
if (ctx.config.autocapture) {
|
|
202
|
+
for (const type of interactionEvents) {
|
|
203
|
+
document.removeEventListener(type, handleInteraction, true);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (patched) {
|
|
207
|
+
history.pushState = origPushState;
|
|
208
|
+
history.replaceState = origReplaceState;
|
|
209
|
+
window.removeEventListener("popstate", emitPageview);
|
|
210
|
+
window.removeEventListener("hashchange", emitPageview);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/channel.ts
|
|
216
|
+
var PAID_MEDIUM_RE = /^(cpc|ppc|paid|display)$/i;
|
|
217
|
+
var SEARCH_ENGINE_DOMAINS = [
|
|
218
|
+
"google.",
|
|
219
|
+
"bing.com",
|
|
220
|
+
"duckduckgo.com",
|
|
221
|
+
"yahoo.",
|
|
222
|
+
"baidu.com",
|
|
223
|
+
"yandex.",
|
|
224
|
+
"ecosia.org",
|
|
225
|
+
"ask.com",
|
|
226
|
+
"aol.com"
|
|
227
|
+
];
|
|
228
|
+
var SOCIAL_DOMAINS = [
|
|
229
|
+
"facebook.com",
|
|
230
|
+
"fb.com",
|
|
231
|
+
"twitter.com",
|
|
232
|
+
"x.com",
|
|
233
|
+
"t.co",
|
|
234
|
+
"linkedin.com",
|
|
235
|
+
"instagram.com",
|
|
236
|
+
"tiktok.com",
|
|
237
|
+
"reddit.com",
|
|
238
|
+
"pinterest.",
|
|
239
|
+
"youtube.com",
|
|
240
|
+
"threads.net"
|
|
241
|
+
];
|
|
242
|
+
var matchesDomain = (domain, list) => list.some((needle) => domain.includes(needle));
|
|
243
|
+
var extractDomain = (url) => {
|
|
244
|
+
if (!url) return void 0;
|
|
245
|
+
try {
|
|
246
|
+
return new URL(url).hostname.toLowerCase();
|
|
247
|
+
} catch {
|
|
248
|
+
return void 0;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
function classifyChannel(referrer, utm, currentHostname) {
|
|
252
|
+
const referrerDomain = extractDomain(referrer);
|
|
253
|
+
const utmMedium = utm.$utm_medium;
|
|
254
|
+
const hasReferrer = Boolean(referrerDomain);
|
|
255
|
+
const hasUtm = Object.keys(utm).length > 0;
|
|
256
|
+
if (!hasReferrer && !hasUtm) return "direct";
|
|
257
|
+
if (utmMedium && PAID_MEDIUM_RE.test(utmMedium)) return "paid";
|
|
258
|
+
if (referrerDomain && matchesDomain(referrerDomain, SEARCH_ENGINE_DOMAINS))
|
|
259
|
+
return "organic search";
|
|
260
|
+
if (referrerDomain && matchesDomain(referrerDomain, SOCIAL_DOMAINS)) return "social";
|
|
261
|
+
if (utmMedium && utmMedium.toLowerCase() === "email") return "email";
|
|
262
|
+
if (referrerDomain && referrerDomain !== currentHostname.toLowerCase()) return "referral";
|
|
263
|
+
return "direct";
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/device.ts
|
|
267
|
+
var BROWSERS = [
|
|
268
|
+
{ name: "Edge", re: /Edg(?:e|A|iOS)?\/(\d+)/ },
|
|
269
|
+
{ name: "Opera", re: /(?:OPR|Opera)\/(\d+)/ },
|
|
270
|
+
{ name: "Samsung Internet", re: /SamsungBrowser\/(\d+)/ },
|
|
271
|
+
{ name: "Firefox", re: /(?:Firefox|FxiOS)\/(\d+)/ },
|
|
272
|
+
{ name: "Chrome", re: /(?:Chrome|CriOS)\/(\d+)/ },
|
|
273
|
+
{ name: "Safari", re: /Version\/(\d+).*Safari/ }
|
|
274
|
+
];
|
|
275
|
+
var detectBrowser = (ua) => {
|
|
276
|
+
for (const b of BROWSERS) {
|
|
277
|
+
const m = ua.match(b.re);
|
|
278
|
+
if (m) return { name: b.name, version: m[1] };
|
|
279
|
+
}
|
|
280
|
+
return {};
|
|
281
|
+
};
|
|
282
|
+
var detectOS = (ua) => {
|
|
283
|
+
if (/Windows/.test(ua)) return "Windows";
|
|
284
|
+
if (/iPhone|iPad|iPod/.test(ua)) return "iOS";
|
|
285
|
+
if (/Android/.test(ua)) return "Android";
|
|
286
|
+
if (/CrOS/.test(ua)) return "Chrome OS";
|
|
287
|
+
if (/Mac OS X|Macintosh/.test(ua)) return "macOS";
|
|
288
|
+
if (/Linux/.test(ua)) return "Linux";
|
|
289
|
+
return void 0;
|
|
290
|
+
};
|
|
291
|
+
var detectDeviceType = (ua) => {
|
|
292
|
+
if (/iPad/.test(ua)) return "Tablet";
|
|
293
|
+
if (/Android/.test(ua)) return /Mobile/.test(ua) ? "Mobile" : "Tablet";
|
|
294
|
+
if (/iPhone|iPod/.test(ua)) return "Mobile";
|
|
295
|
+
if (/Mobile/.test(ua)) return "Mobile";
|
|
296
|
+
return "Desktop";
|
|
297
|
+
};
|
|
298
|
+
function parseDevice(userAgent, language) {
|
|
299
|
+
const props = {};
|
|
300
|
+
const ua = userAgent ?? "";
|
|
301
|
+
if (ua) {
|
|
302
|
+
const browser = detectBrowser(ua);
|
|
303
|
+
if (browser.name) props.$browser = browser.name;
|
|
304
|
+
if (browser.version) props.$browser_version = browser.version;
|
|
305
|
+
const os = detectOS(ua);
|
|
306
|
+
if (os) props.$os = os;
|
|
307
|
+
props.$device_type = detectDeviceType(ua);
|
|
308
|
+
}
|
|
309
|
+
if (language) props.$lang = language;
|
|
310
|
+
return props;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/flags.ts
|
|
314
|
+
var HASH_DENOM = Number(BigInt("0xfffffffffffffff"));
|
|
315
|
+
var FLAGS_STORAGE_KEY = "ckrm_flags";
|
|
316
|
+
var safeLocalStorage = () => {
|
|
317
|
+
try {
|
|
318
|
+
if (typeof localStorage === "undefined") return null;
|
|
319
|
+
return localStorage;
|
|
320
|
+
} catch {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
function createFlagClient(deps) {
|
|
325
|
+
let cache = restoreCache();
|
|
326
|
+
function restoreCache() {
|
|
327
|
+
try {
|
|
328
|
+
const raw = safeLocalStorage()?.getItem(FLAGS_STORAGE_KEY);
|
|
329
|
+
if (!raw) return {};
|
|
330
|
+
const parsed = JSON.parse(raw);
|
|
331
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
334
|
+
return {};
|
|
335
|
+
}
|
|
336
|
+
const persist = (map) => {
|
|
337
|
+
try {
|
|
338
|
+
safeLocalStorage()?.setItem(FLAGS_STORAGE_KEY, JSON.stringify(map));
|
|
339
|
+
} catch {
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
let lastVersion;
|
|
343
|
+
const buildUrl = () => {
|
|
344
|
+
const base = deps.getApiHost().replace(/\/+$/, "");
|
|
345
|
+
return `${base}/v1/flags`;
|
|
346
|
+
};
|
|
347
|
+
const buildVersionUrl = () => {
|
|
348
|
+
const base = deps.getApiHost().replace(/\/+$/, "");
|
|
349
|
+
return `${base}/v1/flags/version`;
|
|
350
|
+
};
|
|
351
|
+
const fetchFlags = async () => {
|
|
352
|
+
try {
|
|
353
|
+
if (typeof fetch !== "function") return cache;
|
|
354
|
+
const res = await fetch(buildUrl(), {
|
|
355
|
+
method: "POST",
|
|
356
|
+
headers: {
|
|
357
|
+
"content-type": "application/json",
|
|
358
|
+
authorization: `Bearer ${deps.getWriteKey()}`
|
|
359
|
+
},
|
|
360
|
+
body: JSON.stringify({
|
|
361
|
+
distinct_id: deps.getDistinctId(),
|
|
362
|
+
person_properties: deps.getPersonProperties()
|
|
363
|
+
})
|
|
364
|
+
});
|
|
365
|
+
if (!res.ok) return cache;
|
|
366
|
+
const data = await res.json();
|
|
367
|
+
const flags = data?.flags;
|
|
368
|
+
if (flags && typeof flags === "object") {
|
|
369
|
+
cache = flags;
|
|
370
|
+
persist(cache);
|
|
371
|
+
}
|
|
372
|
+
return cache;
|
|
373
|
+
} catch {
|
|
374
|
+
return cache;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
const checkVersionAndMaybeFetch = async () => {
|
|
378
|
+
let changed = true;
|
|
379
|
+
let newVersion;
|
|
380
|
+
try {
|
|
381
|
+
if (typeof fetch === "function") {
|
|
382
|
+
const res = await fetch(buildVersionUrl(), {
|
|
383
|
+
method: "GET",
|
|
384
|
+
headers: {
|
|
385
|
+
authorization: `Bearer ${deps.getWriteKey()}`
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
if (res.ok) {
|
|
389
|
+
const data = await res.json();
|
|
390
|
+
newVersion = data?.version;
|
|
391
|
+
changed = lastVersion === void 0 || newVersion !== lastVersion;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
} catch {
|
|
395
|
+
}
|
|
396
|
+
if (!changed) return { changed: false, flags: cache };
|
|
397
|
+
const flags = await fetchFlags();
|
|
398
|
+
if (newVersion !== void 0) lastVersion = newVersion;
|
|
399
|
+
return { changed: true, flags };
|
|
400
|
+
};
|
|
401
|
+
return {
|
|
402
|
+
getCache: () => cache,
|
|
403
|
+
isFeatureEnabled: (key) => cache[key]?.enabled ?? false,
|
|
404
|
+
getFeatureFlag: (key) => {
|
|
405
|
+
const r = cache[key];
|
|
406
|
+
if (!r?.enabled) return false;
|
|
407
|
+
return r.variant != null ? r.variant : true;
|
|
408
|
+
},
|
|
409
|
+
getFeatureFlagPayload: (key) => cache[key]?.payload ?? null,
|
|
410
|
+
fetchFlags,
|
|
411
|
+
checkVersionAndMaybeFetch,
|
|
412
|
+
clear: () => {
|
|
413
|
+
cache = {};
|
|
414
|
+
try {
|
|
415
|
+
safeLocalStorage()?.removeItem(FLAGS_STORAGE_KEY);
|
|
416
|
+
} catch {
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/session.ts
|
|
423
|
+
var IDLE_MS = 30 * 60 * 1e3;
|
|
424
|
+
var MAX_MS = 24 * 60 * 60 * 1e3;
|
|
425
|
+
var SID_KEY = "ckrm_sid";
|
|
426
|
+
var parse = (raw) => {
|
|
427
|
+
if (!raw) return null;
|
|
428
|
+
try {
|
|
429
|
+
const v = JSON.parse(raw);
|
|
430
|
+
if (v && typeof v === "object" && typeof v.id === "string" && typeof v.start === "number" && typeof v.last === "number") {
|
|
431
|
+
return v;
|
|
432
|
+
}
|
|
433
|
+
} catch {
|
|
434
|
+
}
|
|
435
|
+
return null;
|
|
436
|
+
};
|
|
437
|
+
function createSessionManager({ now, storage, genId }) {
|
|
438
|
+
const load = () => {
|
|
439
|
+
try {
|
|
440
|
+
return parse(storage()?.getItem(SID_KEY) ?? null);
|
|
441
|
+
} catch {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
const save = (s) => {
|
|
446
|
+
try {
|
|
447
|
+
storage()?.setItem(SID_KEY, JSON.stringify(s));
|
|
448
|
+
} catch {
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
const start = (t) => {
|
|
452
|
+
const s = { id: genId(), start: t, last: t };
|
|
453
|
+
save(s);
|
|
454
|
+
return s;
|
|
455
|
+
};
|
|
456
|
+
return {
|
|
457
|
+
getSessionId() {
|
|
458
|
+
const t = now();
|
|
459
|
+
const existing = load();
|
|
460
|
+
const expired = !existing || t - existing.last > IDLE_MS || t - existing.start > MAX_MS;
|
|
461
|
+
if (expired) {
|
|
462
|
+
return start(t).id;
|
|
463
|
+
}
|
|
464
|
+
existing.last = t;
|
|
465
|
+
save(existing);
|
|
466
|
+
return existing.id;
|
|
467
|
+
},
|
|
468
|
+
reset() {
|
|
469
|
+
try {
|
|
470
|
+
storage()?.removeItem(SID_KEY);
|
|
471
|
+
} catch {
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/transport.ts
|
|
478
|
+
var FLUSH_SIZE = 20;
|
|
479
|
+
var BATCH_MAX = 100;
|
|
480
|
+
var FLUSH_INTERVAL_MS = 3e3;
|
|
481
|
+
var RETRY_BASE_MS = 1e3;
|
|
482
|
+
var RETRY_CAP_MS = 3e4;
|
|
483
|
+
var RETRY_MAX_EVENTS = 1e3;
|
|
484
|
+
var isBrowser = () => typeof window !== "undefined";
|
|
485
|
+
function createTransport(getConfig, getWriteKey) {
|
|
486
|
+
let buffer = [];
|
|
487
|
+
let retryBuffer = [];
|
|
488
|
+
let retryAttempt = 0;
|
|
489
|
+
let retryTimer = null;
|
|
490
|
+
let interval = null;
|
|
491
|
+
const buildUrl = () => {
|
|
492
|
+
const { api_host } = getConfig();
|
|
493
|
+
const base = api_host.replace(/\/+$/, "");
|
|
494
|
+
return `${base}/v1/capture?token=${encodeURIComponent(getWriteKey())}`;
|
|
495
|
+
};
|
|
496
|
+
const buildBody = (batch) => JSON.stringify({ batch });
|
|
497
|
+
const scheduleRetry = () => {
|
|
498
|
+
if (retryTimer !== null || retryBuffer.length === 0) return;
|
|
499
|
+
const delay = Math.min(RETRY_BASE_MS * 2 ** retryAttempt, RETRY_CAP_MS);
|
|
500
|
+
try {
|
|
501
|
+
retryTimer = setTimeout(() => {
|
|
502
|
+
retryTimer = null;
|
|
503
|
+
flush(false);
|
|
504
|
+
}, delay);
|
|
505
|
+
} catch {
|
|
506
|
+
retryTimer = null;
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
const requeue = (batch) => {
|
|
510
|
+
retryBuffer = retryBuffer.concat(batch);
|
|
511
|
+
if (retryBuffer.length > RETRY_MAX_EVENTS) {
|
|
512
|
+
retryBuffer = retryBuffer.slice(retryBuffer.length - RETRY_MAX_EVENTS);
|
|
513
|
+
}
|
|
514
|
+
retryAttempt = Math.min(retryAttempt + 1, 5);
|
|
515
|
+
scheduleRetry();
|
|
516
|
+
};
|
|
517
|
+
const sendBeacon = (url, body) => {
|
|
518
|
+
try {
|
|
519
|
+
if (typeof navigator === "undefined" || typeof navigator.sendBeacon !== "function") {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
const blob = new Blob([body], { type: "text/plain" });
|
|
523
|
+
return navigator.sendBeacon(url, blob);
|
|
524
|
+
} catch {
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
const reportRejection = async (res, batch) => {
|
|
529
|
+
let detail;
|
|
530
|
+
try {
|
|
531
|
+
detail = await res.json();
|
|
532
|
+
} catch {
|
|
533
|
+
}
|
|
534
|
+
console.warn(
|
|
535
|
+
`[clickroom] capture() batch rejected by server (HTTP ${res.status}); dropping ${batch.length} event(s)`,
|
|
536
|
+
detail
|
|
537
|
+
);
|
|
538
|
+
};
|
|
539
|
+
const sendFetch = (url, body, batch) => {
|
|
540
|
+
try {
|
|
541
|
+
if (typeof fetch !== "function") {
|
|
542
|
+
requeue(batch);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
fetch(url, {
|
|
546
|
+
method: "POST",
|
|
547
|
+
keepalive: true,
|
|
548
|
+
headers: { "content-type": "application/json" },
|
|
549
|
+
body
|
|
550
|
+
}).then((res) => {
|
|
551
|
+
if (res.ok) {
|
|
552
|
+
retryAttempt = 0;
|
|
553
|
+
if (retryBuffer.length > 0) scheduleRetry();
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
if (res.status >= 500) {
|
|
557
|
+
requeue(batch);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
void reportRejection(res, batch);
|
|
561
|
+
}).catch(() => {
|
|
562
|
+
requeue(batch);
|
|
563
|
+
});
|
|
564
|
+
} catch {
|
|
565
|
+
requeue(batch);
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
function flush(useBeacon = false) {
|
|
569
|
+
if (!isBrowser()) {
|
|
570
|
+
buffer = [];
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const pending = retryBuffer.concat(buffer);
|
|
574
|
+
buffer = [];
|
|
575
|
+
retryBuffer = [];
|
|
576
|
+
if (pending.length === 0) return;
|
|
577
|
+
try {
|
|
578
|
+
const url = buildUrl();
|
|
579
|
+
for (let i = 0; i < pending.length; i += BATCH_MAX) {
|
|
580
|
+
const batch = pending.slice(i, i + BATCH_MAX);
|
|
581
|
+
const body = buildBody(batch);
|
|
582
|
+
if (useBeacon && sendBeacon(url, body)) continue;
|
|
583
|
+
if (useBeacon) {
|
|
584
|
+
sendFetch(url, body, batch);
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
sendFetch(url, body, batch);
|
|
588
|
+
}
|
|
589
|
+
} catch {
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
const onVisibilityChange = () => {
|
|
593
|
+
try {
|
|
594
|
+
if (typeof document !== "undefined" && document.hidden) flush(true);
|
|
595
|
+
} catch {
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
const onPageHide = () => {
|
|
599
|
+
flush(true);
|
|
600
|
+
};
|
|
601
|
+
const enqueue = (event) => {
|
|
602
|
+
if (!isBrowser()) return;
|
|
603
|
+
try {
|
|
604
|
+
buffer.push(event);
|
|
605
|
+
if (buffer.length >= FLUSH_SIZE) flush(false);
|
|
606
|
+
} catch {
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
const teardown = () => {
|
|
610
|
+
try {
|
|
611
|
+
if (interval !== null) {
|
|
612
|
+
clearInterval(interval);
|
|
613
|
+
interval = null;
|
|
614
|
+
}
|
|
615
|
+
if (retryTimer !== null) {
|
|
616
|
+
clearTimeout(retryTimer);
|
|
617
|
+
retryTimer = null;
|
|
618
|
+
}
|
|
619
|
+
if (typeof document !== "undefined") {
|
|
620
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
621
|
+
}
|
|
622
|
+
if (typeof window !== "undefined") {
|
|
623
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
624
|
+
window.removeEventListener("beforeunload", onPageHide);
|
|
625
|
+
}
|
|
626
|
+
} catch {
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
if (isBrowser()) {
|
|
630
|
+
try {
|
|
631
|
+
interval = setInterval(() => flush(false), FLUSH_INTERVAL_MS);
|
|
632
|
+
if (typeof document !== "undefined") {
|
|
633
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
634
|
+
}
|
|
635
|
+
window.addEventListener("pagehide", onPageHide);
|
|
636
|
+
window.addEventListener("beforeunload", onPageHide);
|
|
637
|
+
} catch {
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
return { enqueue, flush, teardown };
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// src/utm.ts
|
|
644
|
+
var UTM_KEYS = ["source", "medium", "campaign", "term", "content"];
|
|
645
|
+
function parseUtm(search) {
|
|
646
|
+
const props = {};
|
|
647
|
+
if (!search) return props;
|
|
648
|
+
try {
|
|
649
|
+
const params = new URLSearchParams(search);
|
|
650
|
+
for (const key of UTM_KEYS) {
|
|
651
|
+
const value = params.get(`utm_${key}`);
|
|
652
|
+
if (value) props[`$utm_${key}`] = value;
|
|
653
|
+
}
|
|
654
|
+
} catch {
|
|
655
|
+
}
|
|
656
|
+
return props;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// src/index.ts
|
|
660
|
+
var PACKAGE_NAME = "@clickroom/sdk-js";
|
|
661
|
+
var LIB_NAME = "clickroom-js";
|
|
662
|
+
var LIB_VERSION = "0.1.0";
|
|
663
|
+
var DID_KEY = "ckrm_did";
|
|
664
|
+
var STORAGE_PREFIX = "ckrm_";
|
|
665
|
+
var DEFAULT_CONFIG = {
|
|
666
|
+
api_host: "https://sdk.clickroom.co",
|
|
667
|
+
autocapture: true,
|
|
668
|
+
capture_pageview: true
|
|
669
|
+
};
|
|
670
|
+
var isBrowser2 = () => typeof window !== "undefined";
|
|
671
|
+
var MIN_FLAG_POLL_INTERVAL_MS = 5e3;
|
|
672
|
+
var state = {
|
|
673
|
+
initialized: false,
|
|
674
|
+
warnedNotInit: false,
|
|
675
|
+
writeKey: "",
|
|
676
|
+
config: { ...DEFAULT_CONFIG },
|
|
677
|
+
distinctId: "",
|
|
678
|
+
personProperties: {},
|
|
679
|
+
transport: null,
|
|
680
|
+
session: null,
|
|
681
|
+
sessionContext: null,
|
|
682
|
+
teardownAutocapture: null,
|
|
683
|
+
flagListeners: [],
|
|
684
|
+
flagClient: null,
|
|
685
|
+
flagsLoaded: false,
|
|
686
|
+
emittedFlagCalls: /* @__PURE__ */ new Set(),
|
|
687
|
+
flagPollId: null
|
|
688
|
+
};
|
|
689
|
+
var safeLocalStorage2 = () => {
|
|
690
|
+
try {
|
|
691
|
+
if (typeof localStorage === "undefined") return null;
|
|
692
|
+
return localStorage;
|
|
693
|
+
} catch {
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
var safeSessionStorage = () => {
|
|
698
|
+
try {
|
|
699
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
700
|
+
return sessionStorage;
|
|
701
|
+
} catch {
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
var storageGet = (key) => {
|
|
706
|
+
try {
|
|
707
|
+
return safeLocalStorage2()?.getItem(key) ?? null;
|
|
708
|
+
} catch {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
var storageSet = (key, value) => {
|
|
713
|
+
try {
|
|
714
|
+
safeLocalStorage2()?.setItem(key, value);
|
|
715
|
+
} catch {
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
var storageRemove = (key) => {
|
|
719
|
+
try {
|
|
720
|
+
safeLocalStorage2()?.removeItem(key);
|
|
721
|
+
} catch {
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
var generateId = () => {
|
|
725
|
+
try {
|
|
726
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
727
|
+
return crypto.randomUUID();
|
|
728
|
+
}
|
|
729
|
+
} catch {
|
|
730
|
+
}
|
|
731
|
+
return `ckrm_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 12)}`;
|
|
732
|
+
};
|
|
733
|
+
var loadOrCreateDistinctId = () => {
|
|
734
|
+
const existing = storageGet(DID_KEY);
|
|
735
|
+
if (existing) return existing;
|
|
736
|
+
const fresh = generateId();
|
|
737
|
+
storageSet(DID_KEY, fresh);
|
|
738
|
+
return fresh;
|
|
739
|
+
};
|
|
740
|
+
var baseProperties = () => {
|
|
741
|
+
const props = {
|
|
742
|
+
$lib: LIB_NAME,
|
|
743
|
+
$lib_version: LIB_VERSION
|
|
744
|
+
};
|
|
745
|
+
try {
|
|
746
|
+
if (typeof location !== "undefined") {
|
|
747
|
+
props.$current_url = location.href;
|
|
748
|
+
props.$host = location.host;
|
|
749
|
+
props.$pathname = location.pathname;
|
|
750
|
+
}
|
|
751
|
+
if (typeof document !== "undefined") {
|
|
752
|
+
props.$referrer = document.referrer;
|
|
753
|
+
}
|
|
754
|
+
if (typeof window !== "undefined" && window.screen) {
|
|
755
|
+
props.$screen_height = window.screen.height;
|
|
756
|
+
props.$screen_width = window.screen.width;
|
|
757
|
+
}
|
|
758
|
+
if (typeof window !== "undefined") {
|
|
759
|
+
props.$viewport_height = window.innerHeight;
|
|
760
|
+
props.$viewport_width = window.innerWidth;
|
|
761
|
+
}
|
|
762
|
+
if (typeof navigator !== "undefined") {
|
|
763
|
+
Object.assign(props, parseDevice(navigator.userAgent, navigator.language));
|
|
764
|
+
}
|
|
765
|
+
} catch {
|
|
766
|
+
}
|
|
767
|
+
return props;
|
|
768
|
+
};
|
|
769
|
+
var buildSessionContextProps = () => {
|
|
770
|
+
try {
|
|
771
|
+
const search = typeof location !== "undefined" ? location.search : "";
|
|
772
|
+
const referrer = typeof document !== "undefined" ? document.referrer : "";
|
|
773
|
+
const hostname = typeof location !== "undefined" ? location.hostname : "";
|
|
774
|
+
const utm = parseUtm(search);
|
|
775
|
+
return { ...utm, $channel_type: classifyChannel(referrer, utm, hostname) };
|
|
776
|
+
} catch {
|
|
777
|
+
return {};
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
var getSessionContextProps = (sessionId) => {
|
|
781
|
+
if (!sessionId) return buildSessionContextProps();
|
|
782
|
+
if (!state.sessionContext || state.sessionContext.sessionId !== sessionId) {
|
|
783
|
+
state.sessionContext = { sessionId, props: buildSessionContextProps() };
|
|
784
|
+
}
|
|
785
|
+
return state.sessionContext.props;
|
|
786
|
+
};
|
|
787
|
+
var warnNotInit = () => {
|
|
788
|
+
if (state.warnedNotInit) return;
|
|
789
|
+
state.warnedNotInit = true;
|
|
790
|
+
try {
|
|
791
|
+
console.warn("[clickroom] capture() called before init(); event dropped");
|
|
792
|
+
} catch {
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
var captureInternal = (event, properties, elementsChain) => {
|
|
796
|
+
if (!state.initialized || !state.transport) {
|
|
797
|
+
warnNotInit();
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
try {
|
|
801
|
+
const sessionId = state.session?.getSessionId();
|
|
802
|
+
const sessionProps = sessionId ? { $session_id: sessionId } : {};
|
|
803
|
+
const payload = {
|
|
804
|
+
event,
|
|
805
|
+
distinct_id: state.distinctId,
|
|
806
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
807
|
+
properties: {
|
|
808
|
+
...baseProperties(),
|
|
809
|
+
...sessionProps,
|
|
810
|
+
...getSessionContextProps(sessionId),
|
|
811
|
+
...properties ?? {}
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
if (elementsChain) payload.elements_chain = elementsChain;
|
|
815
|
+
state.transport.enqueue(payload);
|
|
816
|
+
} catch {
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
var flagFlatMap = (cache) => {
|
|
820
|
+
const flat = {};
|
|
821
|
+
for (const [key, r] of Object.entries(cache)) {
|
|
822
|
+
if (!r.enabled) continue;
|
|
823
|
+
flat[key] = r.variant != null ? r.variant : true;
|
|
824
|
+
}
|
|
825
|
+
return flat;
|
|
826
|
+
};
|
|
827
|
+
var fireFlagListeners = () => {
|
|
828
|
+
if (!state.flagClient) return;
|
|
829
|
+
const flat = flagFlatMap(state.flagClient.getCache());
|
|
830
|
+
for (const cb of state.flagListeners) {
|
|
831
|
+
try {
|
|
832
|
+
cb(flat);
|
|
833
|
+
} catch {
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
var refreshFlags = () => {
|
|
838
|
+
if (!state.flagClient) return;
|
|
839
|
+
state.flagClient.checkVersionAndMaybeFetch().then(() => {
|
|
840
|
+
state.flagsLoaded = true;
|
|
841
|
+
fireFlagListeners();
|
|
842
|
+
}).catch(() => {
|
|
843
|
+
});
|
|
844
|
+
};
|
|
845
|
+
var maybeEmitFlagCall = (key, response) => {
|
|
846
|
+
try {
|
|
847
|
+
if (!state.flagsLoaded) return;
|
|
848
|
+
const resp = String(response);
|
|
849
|
+
const dedup = `${key}:${resp}`;
|
|
850
|
+
if (state.emittedFlagCalls.has(dedup)) return;
|
|
851
|
+
state.emittedFlagCalls.add(dedup);
|
|
852
|
+
captureInternal("$feature_flag_called", {
|
|
853
|
+
$feature_flag: key,
|
|
854
|
+
$feature_flag_response: resp
|
|
855
|
+
});
|
|
856
|
+
} catch {
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
function init(writeKey, opts) {
|
|
860
|
+
if (state.initialized) return;
|
|
861
|
+
if (!isBrowser2()) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
state.writeKey = writeKey;
|
|
865
|
+
state.config = {
|
|
866
|
+
api_host: opts?.api_host ?? DEFAULT_CONFIG.api_host,
|
|
867
|
+
autocapture: opts?.autocapture ?? DEFAULT_CONFIG.autocapture,
|
|
868
|
+
capture_pageview: opts?.capture_pageview ?? DEFAULT_CONFIG.capture_pageview
|
|
869
|
+
};
|
|
870
|
+
state.distinctId = loadOrCreateDistinctId();
|
|
871
|
+
state.session = createSessionManager({
|
|
872
|
+
now: () => Date.now(),
|
|
873
|
+
storage: safeSessionStorage,
|
|
874
|
+
genId: generateId
|
|
875
|
+
});
|
|
876
|
+
state.transport = createTransport(
|
|
877
|
+
() => state.config,
|
|
878
|
+
() => state.writeKey
|
|
879
|
+
);
|
|
880
|
+
state.flagClient = createFlagClient({
|
|
881
|
+
getApiHost: () => state.config.api_host,
|
|
882
|
+
getWriteKey: () => state.writeKey,
|
|
883
|
+
getDistinctId: () => state.distinctId,
|
|
884
|
+
getPersonProperties: () => state.personProperties
|
|
885
|
+
});
|
|
886
|
+
state.initialized = true;
|
|
887
|
+
refreshFlags();
|
|
888
|
+
if (opts?.feature_flag_poll_interval) {
|
|
889
|
+
const intervalMs = Math.max(opts.feature_flag_poll_interval, MIN_FLAG_POLL_INTERVAL_MS);
|
|
890
|
+
state.flagPollId = setInterval(() => refreshFlags(), intervalMs);
|
|
891
|
+
}
|
|
892
|
+
if (state.config.autocapture) {
|
|
893
|
+
const ctx = {
|
|
894
|
+
config: state.config,
|
|
895
|
+
capture: captureInternal
|
|
896
|
+
};
|
|
897
|
+
try {
|
|
898
|
+
state.teardownAutocapture = initAutocapture(ctx);
|
|
899
|
+
} catch {
|
|
900
|
+
}
|
|
901
|
+
} else if (state.config.capture_pageview) {
|
|
902
|
+
captureInternal("$pageview");
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
function capture(event, properties) {
|
|
906
|
+
captureInternal(event, properties);
|
|
907
|
+
}
|
|
908
|
+
function identify(distinctId, personProps) {
|
|
909
|
+
if (!state.initialized) {
|
|
910
|
+
warnNotInit();
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
const previousId = state.distinctId;
|
|
914
|
+
state.distinctId = distinctId;
|
|
915
|
+
storageSet(DID_KEY, distinctId);
|
|
916
|
+
if (personProps) state.personProperties = { ...state.personProperties, ...personProps };
|
|
917
|
+
captureInternal("$identify", {
|
|
918
|
+
$set: personProps ?? {},
|
|
919
|
+
...previousId !== distinctId ? { $anon_distinct_id: previousId } : {}
|
|
920
|
+
});
|
|
921
|
+
state.emittedFlagCalls.clear();
|
|
922
|
+
refreshFlags();
|
|
923
|
+
}
|
|
924
|
+
function reset() {
|
|
925
|
+
try {
|
|
926
|
+
const ls = safeLocalStorage2();
|
|
927
|
+
if (ls) {
|
|
928
|
+
const toRemove = [];
|
|
929
|
+
for (let i = 0; i < ls.length; i += 1) {
|
|
930
|
+
const key = ls.key(i);
|
|
931
|
+
if (key?.startsWith(STORAGE_PREFIX)) toRemove.push(key);
|
|
932
|
+
}
|
|
933
|
+
for (const key of toRemove) storageRemove(key);
|
|
934
|
+
}
|
|
935
|
+
} catch {
|
|
936
|
+
}
|
|
937
|
+
state.distinctId = generateId();
|
|
938
|
+
storageSet(DID_KEY, state.distinctId);
|
|
939
|
+
state.session?.reset();
|
|
940
|
+
state.sessionContext = null;
|
|
941
|
+
state.personProperties = {};
|
|
942
|
+
state.flagsLoaded = false;
|
|
943
|
+
state.emittedFlagCalls.clear();
|
|
944
|
+
state.flagClient?.clear();
|
|
945
|
+
}
|
|
946
|
+
function isFeatureEnabled(key) {
|
|
947
|
+
const result = state.flagClient?.isFeatureEnabled(key) ?? false;
|
|
948
|
+
maybeEmitFlagCall(key, result);
|
|
949
|
+
return result;
|
|
950
|
+
}
|
|
951
|
+
function getFeatureFlag(key) {
|
|
952
|
+
const result = state.flagClient?.getFeatureFlag(key) ?? false;
|
|
953
|
+
maybeEmitFlagCall(key, result);
|
|
954
|
+
return result;
|
|
955
|
+
}
|
|
956
|
+
function getFeatureFlagPayload(key) {
|
|
957
|
+
return state.flagClient?.getFeatureFlagPayload(key) ?? null;
|
|
958
|
+
}
|
|
959
|
+
function onFeatureFlags(cb) {
|
|
960
|
+
state.flagListeners.push(cb);
|
|
961
|
+
if (state.flagsLoaded && state.flagClient) {
|
|
962
|
+
try {
|
|
963
|
+
cb(flagFlatMap(state.flagClient.getCache()));
|
|
964
|
+
} catch {
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
function refreshFeatureFlags() {
|
|
969
|
+
refreshFlags();
|
|
970
|
+
}
|
|
971
|
+
function stopFeatureFlagPolling() {
|
|
972
|
+
if (state.flagPollId !== null) {
|
|
973
|
+
clearInterval(state.flagPollId);
|
|
974
|
+
state.flagPollId = null;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
var clickroom = {
|
|
978
|
+
init,
|
|
979
|
+
capture,
|
|
980
|
+
identify,
|
|
981
|
+
reset,
|
|
982
|
+
isFeatureEnabled,
|
|
983
|
+
getFeatureFlag,
|
|
984
|
+
getFeatureFlagPayload,
|
|
985
|
+
onFeatureFlags,
|
|
986
|
+
refreshFeatureFlags,
|
|
987
|
+
stopFeatureFlagPolling
|
|
988
|
+
};
|
|
989
|
+
var index_default = clickroom;
|
|
990
|
+
export {
|
|
991
|
+
PACKAGE_NAME,
|
|
992
|
+
capture,
|
|
993
|
+
index_default as default,
|
|
994
|
+
getFeatureFlag,
|
|
995
|
+
getFeatureFlagPayload,
|
|
996
|
+
identify,
|
|
997
|
+
init,
|
|
998
|
+
isFeatureEnabled,
|
|
999
|
+
onFeatureFlags,
|
|
1000
|
+
refreshFeatureFlags,
|
|
1001
|
+
reset,
|
|
1002
|
+
stopFeatureFlagPolling
|
|
1003
|
+
};
|
|
1004
|
+
//# sourceMappingURL=index.js.map
|