@sightspool/sdk 0.3.0 → 0.4.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/CHANGELOG.md +21 -0
- package/README.md +128 -329
- package/SECURITY.md +16 -0
- package/dist/index.cjs +230 -1225
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -41
- package/dist/index.d.ts +20 -41
- package/dist/index.js +228 -757
- package/dist/index.js.map +1 -1
- package/dist/release.json +21 -0
- package/dist/sdk.global.js +2 -59
- package/dist/sdk.global.js.map +1 -1
- package/dist/source.json +11 -0
- package/package.json +14 -12
- package/site/CNAME +1 -0
- package/site/demo.html +6 -0
- package/site/demo.js +36 -0
- package/site/index.html +74 -0
- package/site/release.js +20 -0
- package/site/releases/0.4.0/2acf3463db7571c459ca01a4516341891bb4c2ec6265962f494ca8b553cc0e9f/release.json +21 -0
- package/site/releases/0.4.0/2acf3463db7571c459ca01a4516341891bb4c2ec6265962f494ca8b553cc0e9f/sdk.global.js +3 -0
- package/site/releases/0.4.0/2acf3463db7571c459ca01a4516341891bb4c2ec6265962f494ca8b553cc0e9f/sdk.global.js.map +1 -0
- package/site/releases/0.4.0/2acf3463db7571c459ca01a4516341891bb4c2ec6265962f494ca8b553cc0e9f/source.json +11 -0
- package/site/releases/0.4.0/781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e/release.json +21 -0
- package/site/releases/0.4.0/781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e/sdk.global.js +3 -0
- package/site/releases/0.4.0/781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e/sdk.global.js.map +1 -0
- package/site/releases/0.4.0/781ab339e13fe18b86b56cc9d84dd308bebf6482a885c8aae46a8b3b84801f6e/source.json +11 -0
- package/site/releases/0.4.0/c21bee307a5c53e3ebb49d21f0f1ef2cce06a4483fa9824122bd6897ad6ca3ac/release.json +21 -0
- package/site/releases/0.4.0/c21bee307a5c53e3ebb49d21f0f1ef2cce06a4483fa9824122bd6897ad6ca3ac/sdk.global.js +3 -0
- package/site/releases/0.4.0/c21bee307a5c53e3ebb49d21f0f1ef2cce06a4483fa9824122bd6897ad6ca3ac/sdk.global.js.map +1 -0
- package/site/releases/0.4.0/c21bee307a5c53e3ebb49d21f0f1ef2cce06a4483fa9824122bd6897ad6ca3ac/source.json +11 -0
- package/site/styles.css +69 -0
- package/site/trust.html +36 -0
- package/dist/chunk-4PADVIIO.js +0 -41
- package/dist/chunk-4PADVIIO.js.map +0 -1
- package/dist/probe-BNGMQQ5C.js +0 -113
- package/dist/probe-BNGMQQ5C.js.map +0 -1
- package/dist/prompt-GDFYC75D.js +0 -139
- package/dist/prompt-GDFYC75D.js.map +0 -1
- package/dist/survey-CICJQ5VI.js +0 -145
- package/dist/survey-CICJQ5VI.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -2,1283 +2,288 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
|
|
5
|
+
// src/research.ts
|
|
6
|
+
var slot = /* @__PURE__ */ Symbol.for("sightspool.research.runtime.v1");
|
|
7
|
+
var host = () => typeof window === "undefined" ? null : window;
|
|
8
|
+
var current = () => {
|
|
9
|
+
var _a;
|
|
10
|
+
return (_a = host()) == null ? void 0 : _a[slot];
|
|
9
11
|
};
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
var EMAIL_RE, LONG_DIGITS_RE, MAX_LABEL_LEN, ATTACH_DISCLOSURE, REDACTED, IGNORE_ATTR;
|
|
47
|
-
var init_privacy = __esm({
|
|
48
|
-
"src/privacy.ts"() {
|
|
49
|
-
EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
|
|
50
|
-
LONG_DIGITS_RE = /\d{6,}/g;
|
|
51
|
-
MAX_LABEL_LEN = 280;
|
|
52
|
-
ATTACH_DISCLOSURE = "Session and account attached automatically.";
|
|
53
|
-
REDACTED = "\u2039redacted\u203A";
|
|
54
|
-
IGNORE_ATTR = "data-sightspool-ignore";
|
|
12
|
+
var eligible = (r) => r.audience === "all_visitors" || r.identified;
|
|
13
|
+
function remove(r) {
|
|
14
|
+
var _a;
|
|
15
|
+
if (r.frame) return;
|
|
16
|
+
(_a = r.button) == null ? void 0 : _a.remove();
|
|
17
|
+
r.button = null;
|
|
18
|
+
r.offer = null;
|
|
19
|
+
}
|
|
20
|
+
function invalidate(r) {
|
|
21
|
+
var _a;
|
|
22
|
+
r.generation += 1;
|
|
23
|
+
(_a = r.pending) == null ? void 0 : _a.abort();
|
|
24
|
+
r.pending = null;
|
|
25
|
+
remove(r);
|
|
26
|
+
}
|
|
27
|
+
function status(r, value) {
|
|
28
|
+
r.status = value;
|
|
29
|
+
}
|
|
30
|
+
function expand(r, expanded) {
|
|
31
|
+
var _a;
|
|
32
|
+
if (!r.panel || !r.frame || !r.button) return;
|
|
33
|
+
r.expanded = expanded;
|
|
34
|
+
r.panel.hidden = !expanded;
|
|
35
|
+
r.panel.style.display = expanded ? "flex" : "none";
|
|
36
|
+
r.button.hidden = expanded;
|
|
37
|
+
r.button.setAttribute("aria-expanded", String(expanded));
|
|
38
|
+
if (expanded) {
|
|
39
|
+
(_a = r.panel.querySelector("button")) == null ? void 0 : _a.focus();
|
|
40
|
+
} else r.button.focus();
|
|
41
|
+
}
|
|
42
|
+
function openPanel(r) {
|
|
43
|
+
if (r.frame) {
|
|
44
|
+
expand(r, true);
|
|
45
|
+
return;
|
|
55
46
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const x = document.createElement("button");
|
|
93
|
-
x.className = "x";
|
|
94
|
-
x.textContent = "\xD7";
|
|
95
|
-
x.setAttribute("aria-label", "Dismiss");
|
|
96
|
-
x.onclick = () => finish({ answer: "dismissed" });
|
|
97
|
-
card.appendChild(x);
|
|
98
|
-
card.appendChild(node);
|
|
99
|
-
const disc = document.createElement("p");
|
|
100
|
-
disc.className = "disc";
|
|
101
|
-
disc.textContent = ATTACH_DISCLOSURE;
|
|
102
|
-
card.appendChild(disc);
|
|
103
|
-
wrap.appendChild(card);
|
|
104
|
-
}
|
|
105
|
-
function stateA() {
|
|
106
|
-
const node = document.createElement("div");
|
|
107
|
-
const q = document.createElement("p");
|
|
108
|
-
q.className = "q";
|
|
109
|
-
q.textContent = "Were you able to do what you came here to do today?";
|
|
110
|
-
node.appendChild(q);
|
|
111
|
-
const row = document.createElement("div");
|
|
112
|
-
row.className = "row";
|
|
113
|
-
const yes = document.createElement("button");
|
|
114
|
-
yes.className = "primary";
|
|
115
|
-
yes.textContent = "Yes";
|
|
116
|
-
yes.onclick = () => finish({ answer: "yes" });
|
|
117
|
-
const no = document.createElement("button");
|
|
118
|
-
no.textContent = "Not really";
|
|
119
|
-
no.onclick = () => stateB();
|
|
120
|
-
row.append(yes, no);
|
|
121
|
-
node.appendChild(row);
|
|
122
|
-
render(node);
|
|
123
|
-
}
|
|
124
|
-
function stateB() {
|
|
125
|
-
const node = document.createElement("div");
|
|
126
|
-
const q = document.createElement("p");
|
|
127
|
-
q.className = "q";
|
|
128
|
-
q.textContent = "What were you trying to do?";
|
|
129
|
-
node.appendChild(q);
|
|
130
|
-
for (const c of opts.candidates.slice(0, 4)) {
|
|
131
|
-
const b = document.createElement("button");
|
|
132
|
-
b.className = "cand";
|
|
133
|
-
b.textContent = c;
|
|
134
|
-
b.onclick = () => finish({ answer: "not_really", intent: c });
|
|
135
|
-
node.appendChild(b);
|
|
136
|
-
}
|
|
137
|
-
const se = document.createElement("button");
|
|
138
|
-
se.className = "cand";
|
|
139
|
-
se.textContent = "Something else\u2026";
|
|
140
|
-
se.onclick = () => stateText();
|
|
141
|
-
node.appendChild(se);
|
|
142
|
-
render(node);
|
|
143
|
-
}
|
|
144
|
-
function stateText() {
|
|
145
|
-
const node = document.createElement("div");
|
|
146
|
-
const q = document.createElement("p");
|
|
147
|
-
q.className = "q";
|
|
148
|
-
q.textContent = "What were you trying to do?";
|
|
149
|
-
node.appendChild(q);
|
|
150
|
-
const input = document.createElement("input");
|
|
151
|
-
input.type = "text";
|
|
152
|
-
input.placeholder = "In your own words\u2026";
|
|
153
|
-
node.appendChild(input);
|
|
154
|
-
const row = document.createElement("div");
|
|
155
|
-
row.className = "row";
|
|
156
|
-
const submit = document.createElement("button");
|
|
157
|
-
submit.className = "primary";
|
|
158
|
-
submit.textContent = "Send";
|
|
159
|
-
submit.onclick = () => finish({ answer: "not_really", verbatim: input.value.trim() || void 0 });
|
|
160
|
-
row.appendChild(submit);
|
|
161
|
-
node.appendChild(row);
|
|
162
|
-
render(node);
|
|
163
|
-
try {
|
|
164
|
-
input.focus();
|
|
165
|
-
} catch (e) {
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
try {
|
|
169
|
-
document.body.appendChild(host);
|
|
170
|
-
stateA();
|
|
171
|
-
} catch (e) {
|
|
172
|
-
finish({ answer: "dismissed" });
|
|
47
|
+
if (!r.offer || !r.button) return;
|
|
48
|
+
const panel = document.createElement("section");
|
|
49
|
+
panel.id = "sightspool-interview-panel";
|
|
50
|
+
panel.setAttribute("role", "dialog");
|
|
51
|
+
panel.setAttribute("aria-label", "Sightspool interview");
|
|
52
|
+
panel.style.cssText = "position:fixed;bottom:16px;right:16px;z-index:2147483001;box-sizing:border-box;width:420px;max-width:calc(100% - 32px);height:680px;max-height:calc(100dvh - 32px);border:1px solid #e6dfe3;border-radius:20px;background:#fff;color:#262024;box-shadow:0 12px 50px #0003;overflow:hidden;font:14px system-ui;display:flex;flex-direction:column";
|
|
53
|
+
const header = document.createElement("div");
|
|
54
|
+
header.style.cssText = "display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid #eee;flex-shrink:0";
|
|
55
|
+
const title = document.createElement("strong");
|
|
56
|
+
title.textContent = "Sightspool";
|
|
57
|
+
const minimize = document.createElement("button");
|
|
58
|
+
minimize.type = "button";
|
|
59
|
+
minimize.textContent = "Minimize";
|
|
60
|
+
minimize.style.cssText = "font:inherit;color:inherit;background:#fff;border:1px solid #d6cbd1;border-radius:8px;padding:8px 12px;cursor:pointer";
|
|
61
|
+
const hide = () => {
|
|
62
|
+
expand(r, false);
|
|
63
|
+
};
|
|
64
|
+
minimize.onclick = hide;
|
|
65
|
+
header.appendChild(title);
|
|
66
|
+
header.appendChild(minimize);
|
|
67
|
+
panel.appendChild(header);
|
|
68
|
+
const frame = document.createElement("iframe");
|
|
69
|
+
frame.title = "Sightspool research conversation";
|
|
70
|
+
frame.allow = "microphone; autoplay";
|
|
71
|
+
frame.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
|
|
72
|
+
frame.referrerPolicy = "no-referrer";
|
|
73
|
+
frame.style.cssText = "display:block;width:100%;flex:1;min-height:0;border:0;background:#fff";
|
|
74
|
+
const url = new URL(r.endpoint + "/interview-widget");
|
|
75
|
+
url.searchParams.set("key", r.key);
|
|
76
|
+
url.hash = new URLSearchParams({ offer: r.offer, device: r.device }).toString();
|
|
77
|
+
frame.src = url.href;
|
|
78
|
+
panel.appendChild(frame);
|
|
79
|
+
panel.onkeydown = (event) => {
|
|
80
|
+
if (event.key === "Escape") {
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
hide();
|
|
173
83
|
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
.
|
|
183
|
-
|
|
184
|
-
.
|
|
185
|
-
|
|
186
|
-
.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
// src/probe.ts
|
|
203
|
-
var probe_exports = {};
|
|
204
|
-
__export(probe_exports, {
|
|
205
|
-
showProbe: () => showProbe
|
|
206
|
-
});
|
|
207
|
-
function findSlot(slot) {
|
|
84
|
+
};
|
|
85
|
+
r.panel = panel;
|
|
86
|
+
r.frame = frame;
|
|
87
|
+
r.message = (event) => {
|
|
88
|
+
var _a;
|
|
89
|
+
if (event.source !== frame.contentWindow || event.origin !== r.endpoint || ((_a = event.data) == null ? void 0 : _a.type) !== "sightspool:panel:minimize") return;
|
|
90
|
+
hide();
|
|
91
|
+
};
|
|
92
|
+
window.addEventListener("message", r.message);
|
|
93
|
+
r.button.textContent = "Return to interview";
|
|
94
|
+
r.button.setAttribute("aria-label", "Sightspool: return to your interview");
|
|
95
|
+
r.button.setAttribute("aria-controls", panel.id);
|
|
96
|
+
r.button.onclick = () => {
|
|
97
|
+
expand(r, true);
|
|
98
|
+
};
|
|
99
|
+
document.body.appendChild(panel);
|
|
100
|
+
expand(r, true);
|
|
101
|
+
}
|
|
102
|
+
async function check(r) {
|
|
103
|
+
if (r.frame || r.disposed || r.paused || !eligible(r) || r.pending || document.visibilityState !== "visible") return;
|
|
104
|
+
const generation = r.generation;
|
|
105
|
+
const request = new AbortController();
|
|
106
|
+
r.pending = request;
|
|
107
|
+
const timeout = window.setTimeout(() => request.abort(), 1e4);
|
|
108
|
+
if (!r.button) status(r, "checking");
|
|
208
109
|
try {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
let settled = false;
|
|
226
|
-
const inline = !!anchor;
|
|
227
|
-
const host = document.createElement(inline ? "span" : "div");
|
|
228
|
-
const shadow = host.attachShadow({ mode: "open" });
|
|
229
|
-
function teardown() {
|
|
230
|
-
try {
|
|
231
|
-
host.remove();
|
|
232
|
-
} catch (e) {
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
function finish(result, disclose) {
|
|
236
|
-
if (settled) return;
|
|
237
|
-
settled = true;
|
|
238
|
-
resolve(result);
|
|
239
|
-
if (disclose) {
|
|
240
|
-
disclosure();
|
|
241
|
-
setTimeout(teardown, DISCLOSURE_MS);
|
|
242
|
-
} else {
|
|
243
|
-
teardown();
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
const style = document.createElement("style");
|
|
247
|
-
style.textContent = inline ? INLINE_STYLE : FLOATING_STYLE;
|
|
248
|
-
shadow.appendChild(style);
|
|
249
|
-
const wrap = document.createElement(inline ? "span" : "div");
|
|
250
|
-
if (!inline) wrap.className = "wrap";
|
|
251
|
-
shadow.appendChild(wrap);
|
|
252
|
-
function container() {
|
|
253
|
-
wrap.innerHTML = "";
|
|
254
|
-
if (inline) return wrap;
|
|
255
|
-
const c = document.createElement("div");
|
|
256
|
-
c.className = "card";
|
|
257
|
-
wrap.appendChild(c);
|
|
258
|
-
return c;
|
|
259
|
-
}
|
|
260
|
-
function probe() {
|
|
261
|
-
const c = container();
|
|
262
|
-
if (!inline) {
|
|
263
|
-
const x = document.createElement("button");
|
|
264
|
-
x.className = "x";
|
|
265
|
-
x.textContent = "\xD7";
|
|
266
|
-
x.setAttribute("aria-label", "Dismiss");
|
|
267
|
-
x.onclick = () => finish({ dismissed: true, mounted: true }, false);
|
|
268
|
-
c.appendChild(x);
|
|
269
|
-
}
|
|
270
|
-
const b = document.createElement("button");
|
|
271
|
-
b.className = "probe";
|
|
272
|
-
b.textContent = config.label;
|
|
273
|
-
b.onclick = () => finish({ voted: true, mounted: true }, true);
|
|
274
|
-
c.appendChild(b);
|
|
275
|
-
}
|
|
276
|
-
function disclosure() {
|
|
277
|
-
const c = container();
|
|
278
|
-
const p = document.createElement("p");
|
|
279
|
-
p.className = "done";
|
|
280
|
-
p.textContent = config.disclosure;
|
|
281
|
-
c.appendChild(p);
|
|
282
|
-
}
|
|
283
|
-
try {
|
|
284
|
-
(anchor != null ? anchor : document.body).appendChild(host);
|
|
285
|
-
probe();
|
|
286
|
-
} catch (e) {
|
|
287
|
-
finish({ dismissed: true, mounted: false }, false);
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
var FLOATING_STYLE, INLINE_STYLE, DISCLOSURE_MS;
|
|
292
|
-
var init_probe = __esm({
|
|
293
|
-
"src/probe.ts"() {
|
|
294
|
-
FLOATING_STYLE = `
|
|
295
|
-
:host { all: initial; }
|
|
296
|
-
.wrap { position: fixed; bottom: 20px; right: 20px; z-index: 2147483000;
|
|
297
|
-
width: 340px; max-width: calc(100vw - 32px); font-family: system-ui, -apple-system, sans-serif; }
|
|
298
|
-
.card { position: relative; background: #fff; color: #18181b; border: 1px solid #e4e4e7;
|
|
299
|
-
border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,.12); padding: 16px; }
|
|
300
|
-
.probe { font: inherit; font-size: 13px; cursor: pointer; display: block; width: 100%;
|
|
301
|
-
text-align: left; border: 1px dashed #a1a1aa; border-radius: 8px; padding: 10px 12px;
|
|
302
|
-
background: #fafafa; color: #3f3f46; }
|
|
303
|
-
.probe:hover { border-color: #18181b; color: #18181b; }
|
|
304
|
-
.x { position: absolute; top: 8px; right: 10px; border: none; background: none; font-size: 16px;
|
|
305
|
-
color: #a1a1aa; padding: 2px 6px; cursor: pointer; }
|
|
306
|
-
.done { font-size: 13px; color: #18181b; margin: 0; line-height: 1.4; }
|
|
307
|
-
`;
|
|
308
|
-
INLINE_STYLE = `
|
|
309
|
-
:host { all: initial; display: inline-block; max-width: 100%; }
|
|
310
|
-
.probe { font: inherit; font-family: system-ui, -apple-system, sans-serif; font-size: 13px;
|
|
311
|
-
cursor: pointer; display: inline-block; text-align: left; border: 1px dashed #a1a1aa;
|
|
312
|
-
border-radius: 8px; padding: 8px 12px; background: transparent; color: #52525b; }
|
|
313
|
-
.probe:hover { border-color: #18181b; color: #18181b; }
|
|
314
|
-
.done { font-family: system-ui, -apple-system, sans-serif; font-size: 13px; color: #52525b;
|
|
315
|
-
margin: 0; line-height: 1.4; border: 1px dashed #d4d4d8; border-radius: 8px; padding: 8px 12px; }
|
|
316
|
-
`;
|
|
317
|
-
DISCLOSURE_MS = 3200;
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
// src/survey.ts
|
|
322
|
-
var survey_exports = {};
|
|
323
|
-
__export(survey_exports, {
|
|
324
|
-
showSurvey: () => showSurvey
|
|
325
|
-
});
|
|
326
|
-
function showSurvey(config) {
|
|
327
|
-
return new Promise((resolve) => {
|
|
328
|
-
if (typeof document === "undefined") {
|
|
329
|
-
resolve({ dismissed: true });
|
|
110
|
+
const response = await fetch(r.endpoint + "/widget-offer", {
|
|
111
|
+
method: "POST",
|
|
112
|
+
credentials: "omit",
|
|
113
|
+
cache: "no-store",
|
|
114
|
+
referrerPolicy: "no-referrer",
|
|
115
|
+
headers: { "Content-Type": "application/json" },
|
|
116
|
+
body: JSON.stringify({ operation: "offer", key: r.key, device: r.device }),
|
|
117
|
+
signal: request.signal
|
|
118
|
+
});
|
|
119
|
+
if (!response.ok) throw Error("offer unavailable");
|
|
120
|
+
const result = await response.json();
|
|
121
|
+
if (r.disposed || generation !== r.generation || r.paused || !eligible(r)) return;
|
|
122
|
+
if (result.available !== true || typeof result.offer !== "string" || !result.offer) {
|
|
123
|
+
remove(r);
|
|
124
|
+
status(r, "unavailable");
|
|
330
125
|
return;
|
|
331
126
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
function finish(result, thank) {
|
|
342
|
-
if (settled) return;
|
|
343
|
-
settled = true;
|
|
344
|
-
resolve(result);
|
|
345
|
-
if (thank) {
|
|
346
|
-
thankYou();
|
|
347
|
-
setTimeout(teardown, THANKS_MS);
|
|
348
|
-
} else {
|
|
349
|
-
teardown();
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const style = document.createElement("style");
|
|
353
|
-
style.textContent = STYLE2;
|
|
354
|
-
shadow.appendChild(style);
|
|
355
|
-
const wrap = document.createElement("div");
|
|
356
|
-
wrap.className = "wrap";
|
|
357
|
-
shadow.appendChild(wrap);
|
|
358
|
-
function card() {
|
|
359
|
-
wrap.innerHTML = "";
|
|
360
|
-
const c = document.createElement("div");
|
|
361
|
-
c.className = "card";
|
|
362
|
-
wrap.appendChild(c);
|
|
363
|
-
return c;
|
|
364
|
-
}
|
|
365
|
-
function withClose(c) {
|
|
366
|
-
const x = document.createElement("button");
|
|
367
|
-
x.className = "x";
|
|
368
|
-
x.textContent = "\xD7";
|
|
369
|
-
x.setAttribute("aria-label", "Dismiss");
|
|
370
|
-
x.onclick = () => finish({ dismissed: true }, false);
|
|
371
|
-
c.appendChild(x);
|
|
372
|
-
}
|
|
373
|
-
function question() {
|
|
374
|
-
var _a2, _b, _c;
|
|
375
|
-
const c = card();
|
|
376
|
-
withClose(c);
|
|
377
|
-
const q = document.createElement("p");
|
|
378
|
-
q.className = "q";
|
|
379
|
-
q.textContent = config.question;
|
|
380
|
-
c.appendChild(q);
|
|
381
|
-
for (const opt of ((_a2 = config.options) != null ? _a2 : []).slice(0, 6)) {
|
|
382
|
-
const b = document.createElement("button");
|
|
383
|
-
b.className = "opt";
|
|
384
|
-
b.textContent = opt;
|
|
385
|
-
b.onclick = () => finish({ choice: opt }, true);
|
|
386
|
-
c.appendChild(b);
|
|
387
|
-
}
|
|
388
|
-
if (config.allow_text) {
|
|
389
|
-
const se = document.createElement("button");
|
|
390
|
-
se.className = "opt";
|
|
391
|
-
se.textContent = ((_c = (_b = config.options) == null ? void 0 : _b.length) != null ? _c : 0) > 0 ? "Something else\u2026" : "Answer\u2026";
|
|
392
|
-
se.onclick = freeText;
|
|
393
|
-
c.appendChild(se);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
function freeText() {
|
|
397
|
-
const c = card();
|
|
398
|
-
withClose(c);
|
|
399
|
-
const q = document.createElement("p");
|
|
400
|
-
q.className = "q";
|
|
401
|
-
q.textContent = config.question;
|
|
402
|
-
c.appendChild(q);
|
|
403
|
-
const input = document.createElement("input");
|
|
404
|
-
input.type = "text";
|
|
405
|
-
input.placeholder = "In your own words\u2026";
|
|
406
|
-
c.appendChild(input);
|
|
407
|
-
const row = document.createElement("div");
|
|
408
|
-
row.className = "row";
|
|
409
|
-
const send = document.createElement("button");
|
|
410
|
-
send.className = "primary";
|
|
411
|
-
send.textContent = "Send";
|
|
412
|
-
const submit = () => {
|
|
413
|
-
const text = input.value.trim();
|
|
414
|
-
if (!text) {
|
|
415
|
-
finish({ dismissed: true }, false);
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
finish({ text }, true);
|
|
419
|
-
};
|
|
420
|
-
send.onclick = submit;
|
|
421
|
-
input.onkeydown = (e) => {
|
|
422
|
-
if (e.key === "Enter") submit();
|
|
423
|
-
};
|
|
424
|
-
row.appendChild(send);
|
|
425
|
-
c.appendChild(row);
|
|
426
|
-
try {
|
|
427
|
-
input.focus();
|
|
428
|
-
} catch (e) {
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
function thankYou() {
|
|
432
|
-
const c = card();
|
|
433
|
-
const p = document.createElement("p");
|
|
434
|
-
p.className = "done";
|
|
435
|
-
p.textContent = "Thanks \u2014 that helps.";
|
|
436
|
-
c.appendChild(p);
|
|
437
|
-
}
|
|
438
|
-
try {
|
|
439
|
-
document.body.appendChild(host);
|
|
440
|
-
question();
|
|
441
|
-
} catch (e) {
|
|
442
|
-
finish({ dismissed: true }, false);
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
var STYLE2, THANKS_MS;
|
|
447
|
-
var init_survey = __esm({
|
|
448
|
-
"src/survey.ts"() {
|
|
449
|
-
STYLE2 = `
|
|
450
|
-
:host { all: initial; }
|
|
451
|
-
.wrap { position: fixed; bottom: 20px; right: 20px; z-index: 2147483000;
|
|
452
|
-
width: 340px; max-width: calc(100vw - 32px); font-family: system-ui, -apple-system, sans-serif; }
|
|
453
|
-
.card { position: relative; background: #fff; color: #18181b; border: 1px solid #e4e4e7;
|
|
454
|
-
border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,.12); padding: 16px; }
|
|
455
|
-
.q { font-size: 14px; font-weight: 600; margin: 0 0 12px; line-height: 1.35; padding-right: 16px; }
|
|
456
|
-
.row { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
457
|
-
button { font: inherit; font-size: 13px; cursor: pointer; border-radius: 8px; padding: 8px 12px;
|
|
458
|
-
border: 1px solid #e4e4e7; background: #fafafa; color: #18181b; }
|
|
459
|
-
button:hover { background: #f4f4f5; }
|
|
460
|
-
button.primary { background: #18181b; color: #fff; border-color: #18181b; }
|
|
461
|
-
.opt { display: block; width: 100%; text-align: left; margin-bottom: 6px; }
|
|
462
|
-
.x { position: absolute; top: 8px; right: 10px; border: none; background: none; font-size: 16px;
|
|
463
|
-
color: #a1a1aa; padding: 2px 6px; cursor: pointer; }
|
|
464
|
-
.done { font-size: 13px; color: #18181b; margin: 0; }
|
|
465
|
-
input { font: inherit; font-size: 13px; width: 100%; box-sizing: border-box; padding: 8px 10px;
|
|
466
|
-
border: 1px solid #e4e4e7; border-radius: 8px; margin-bottom: 8px; }
|
|
467
|
-
`;
|
|
468
|
-
THANKS_MS = 1100;
|
|
469
|
-
}
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
// src/capture.ts
|
|
473
|
-
init_privacy();
|
|
474
|
-
var TRAIL_MAX = 30;
|
|
475
|
-
var DEAD_CLICK_MS = 700;
|
|
476
|
-
var RAGE_WINDOW_MS = 1e3;
|
|
477
|
-
var RAGE_COUNT = 3;
|
|
478
|
-
function hasDom() {
|
|
479
|
-
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
480
|
-
}
|
|
481
|
-
function createCapture(opts) {
|
|
482
|
-
var _a2, _b;
|
|
483
|
-
const redactSelectors = (_a2 = opts.redact) != null ? _a2 : [];
|
|
484
|
-
const blockSelectors = (_b = opts.block) != null ? _b : [];
|
|
485
|
-
let buf = [];
|
|
486
|
-
let lastError;
|
|
487
|
-
let started = false;
|
|
488
|
-
const subs = [];
|
|
489
|
-
const cleanups = [];
|
|
490
|
-
let recentClicks = [];
|
|
491
|
-
function notify() {
|
|
492
|
-
for (const cb of subs) {
|
|
493
|
-
try {
|
|
494
|
-
cb();
|
|
495
|
-
} catch (e) {
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
function push(type, label) {
|
|
500
|
-
try {
|
|
501
|
-
buf.push({ t: Date.now(), type, label: label ? redactText(label) : void 0 });
|
|
502
|
-
if (buf.length > TRAIL_MAX) buf = buf.slice(-TRAIL_MAX);
|
|
503
|
-
if (opts.debug) console.debug("[sightspool] event", type, label);
|
|
504
|
-
notify();
|
|
505
|
-
} catch (e) {
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
function currentRoute() {
|
|
509
|
-
try {
|
|
510
|
-
return location.pathname + location.search;
|
|
511
|
-
} catch (e) {
|
|
512
|
-
return "";
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
function currentLabel() {
|
|
516
|
-
try {
|
|
517
|
-
return document.title || "";
|
|
518
|
-
} catch (e) {
|
|
519
|
-
return "";
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
function onClick(ev) {
|
|
523
|
-
var _a3;
|
|
524
|
-
try {
|
|
525
|
-
const target = ev.target;
|
|
526
|
-
if (isIgnoredElement(target, blockSelectors)) return;
|
|
527
|
-
const text = target && (((_a3 = target.getAttribute) == null ? void 0 : _a3.call(target, "aria-label")) || target.textContent) || "";
|
|
528
|
-
const label = matchesRedactSelector(target, redactSelectors) ? REDACTED : text.trim().slice(0, 60);
|
|
529
|
-
const t = Date.now();
|
|
530
|
-
recentClicks = recentClicks.filter((ts) => t - ts <= RAGE_WINDOW_MS);
|
|
531
|
-
recentClicks.push(t);
|
|
532
|
-
const isRage = recentClicks.length >= RAGE_COUNT;
|
|
533
|
-
push(isRage ? "rage_click" : "click", label);
|
|
534
|
-
const routeBefore = currentRoute();
|
|
535
|
-
let mutated = false;
|
|
536
|
-
let observer = null;
|
|
127
|
+
r.offer = result.offer;
|
|
128
|
+
status(r, "available");
|
|
129
|
+
if (r.button) return;
|
|
130
|
+
const button = document.createElement("button");
|
|
131
|
+
button.type = "button";
|
|
132
|
+
button.textContent = "Share your experience \xB7 5 min";
|
|
133
|
+
button.setAttribute("aria-label", "Sightspool: join a five-minute user interview");
|
|
134
|
+
button.style.cssText = "position:fixed;bottom:20px;right:20px;z-index:2147483000;padding:14px 18px;border:0;border-radius:999px;background:#ad1668;color:white;font:500 14px system-ui;box-shadow:0 8px 30px #0003;cursor:pointer;max-width:calc(100vw - 40px)";
|
|
135
|
+
button.onclick = () => {
|
|
537
136
|
try {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
});
|
|
541
|
-
observer.observe(document.body, { childList: true, subtree: true, attributes: true });
|
|
137
|
+
if (r.disposed || r.paused || !eligible(r) || !r.offer) return;
|
|
138
|
+
openPanel(r);
|
|
542
139
|
} catch (e) {
|
|
543
140
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
}
|
|
552
|
-
}, DEAD_CLICK_MS);
|
|
553
|
-
} catch (e) {
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
function onError(ev) {
|
|
557
|
-
try {
|
|
558
|
-
lastError = String(ev.message || "error");
|
|
559
|
-
push("error", lastError);
|
|
560
|
-
} catch (e) {
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
function onRejection(ev) {
|
|
564
|
-
try {
|
|
565
|
-
const reason = ev.reason;
|
|
566
|
-
lastError = reason instanceof Error ? reason.message : String(reason);
|
|
567
|
-
push("error", lastError);
|
|
568
|
-
} catch (e) {
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
function installRouteTracking() {
|
|
572
|
-
try {
|
|
573
|
-
const fire = () => push("route", currentRoute());
|
|
574
|
-
const origPush = history.pushState;
|
|
575
|
-
const origReplace = history.replaceState;
|
|
576
|
-
history.pushState = function(...args) {
|
|
577
|
-
const r = origPush.apply(this, args);
|
|
578
|
-
fire();
|
|
579
|
-
return r;
|
|
580
|
-
};
|
|
581
|
-
history.replaceState = function(...args) {
|
|
582
|
-
const r = origReplace.apply(this, args);
|
|
583
|
-
fire();
|
|
584
|
-
return r;
|
|
585
|
-
};
|
|
586
|
-
window.addEventListener("popstate", fire);
|
|
587
|
-
cleanups.push(() => {
|
|
588
|
-
history.pushState = origPush;
|
|
589
|
-
history.replaceState = origReplace;
|
|
590
|
-
window.removeEventListener("popstate", fire);
|
|
591
|
-
});
|
|
592
|
-
push("route", currentRoute());
|
|
593
|
-
} catch (e) {
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
function installFetchTracking() {
|
|
597
|
-
try {
|
|
598
|
-
if (typeof window.fetch !== "function") return;
|
|
599
|
-
const orig = window.fetch.bind(window);
|
|
600
|
-
window.fetch = async (...args) => {
|
|
601
|
-
try {
|
|
602
|
-
const res = await orig(...args);
|
|
603
|
-
if (!res.ok) push("request_error", `HTTP ${res.status}`);
|
|
604
|
-
return res;
|
|
605
|
-
} catch (err) {
|
|
606
|
-
push("request_error", err instanceof Error ? err.message : "network error");
|
|
607
|
-
throw err;
|
|
608
|
-
}
|
|
609
|
-
};
|
|
610
|
-
cleanups.push(() => {
|
|
611
|
-
window.fetch = orig;
|
|
612
|
-
});
|
|
613
|
-
} catch (e) {
|
|
141
|
+
};
|
|
142
|
+
r.button = button;
|
|
143
|
+
document.body.appendChild(button);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
if (!r.disposed && generation === r.generation) {
|
|
146
|
+
remove(r);
|
|
147
|
+
status(r, "error");
|
|
614
148
|
}
|
|
149
|
+
} finally {
|
|
150
|
+
window.clearTimeout(timeout);
|
|
151
|
+
if (r.pending === request) r.pending = null;
|
|
615
152
|
}
|
|
616
|
-
return {
|
|
617
|
-
start() {
|
|
618
|
-
if (started || !hasDom()) return;
|
|
619
|
-
started = true;
|
|
620
|
-
try {
|
|
621
|
-
document.addEventListener("click", onClick, true);
|
|
622
|
-
window.addEventListener("error", onError);
|
|
623
|
-
window.addEventListener("unhandledrejection", onRejection);
|
|
624
|
-
cleanups.push(() => {
|
|
625
|
-
document.removeEventListener("click", onClick, true);
|
|
626
|
-
window.removeEventListener("error", onError);
|
|
627
|
-
window.removeEventListener("unhandledrejection", onRejection);
|
|
628
|
-
});
|
|
629
|
-
installRouteTracking();
|
|
630
|
-
installFetchTracking();
|
|
631
|
-
} catch (e) {
|
|
632
|
-
}
|
|
633
|
-
},
|
|
634
|
-
stop() {
|
|
635
|
-
started = false;
|
|
636
|
-
for (const c of cleanups.splice(0)) {
|
|
637
|
-
try {
|
|
638
|
-
c();
|
|
639
|
-
} catch (e) {
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
},
|
|
643
|
-
trail() {
|
|
644
|
-
return buf.slice();
|
|
645
|
-
},
|
|
646
|
-
route: currentRoute,
|
|
647
|
-
label: currentLabel,
|
|
648
|
-
error() {
|
|
649
|
-
return lastError;
|
|
650
|
-
},
|
|
651
|
-
onEvent(cb) {
|
|
652
|
-
subs.push(cb);
|
|
653
|
-
},
|
|
654
|
-
recordSearch(query, zeroResult) {
|
|
655
|
-
const q = query.trim();
|
|
656
|
-
if (!q) return;
|
|
657
|
-
push(zeroResult ? "zero_result" : "search", q);
|
|
658
|
-
}
|
|
659
|
-
};
|
|
660
153
|
}
|
|
661
|
-
|
|
662
|
-
// src/harvest.ts
|
|
663
|
-
init_privacy();
|
|
664
|
-
var SEARCH_HINT_RE = /search|filter|find|query|command|lookup/i;
|
|
665
|
-
var DEBOUNCE_MS = 800;
|
|
666
|
-
function looksLikeSearch(el) {
|
|
154
|
+
function init(config) {
|
|
667
155
|
try {
|
|
668
|
-
const
|
|
669
|
-
if (
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
return SEARCH_HINT_RE.test(hay);
|
|
680
|
-
} catch (e) {
|
|
681
|
-
return false;
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
function createHarvest(capture, opts) {
|
|
685
|
-
var _a2, _b;
|
|
686
|
-
let hint;
|
|
687
|
-
let timer = null;
|
|
688
|
-
let started = false;
|
|
689
|
-
const redactSelectors = (_a2 = opts.redact) != null ? _a2 : [];
|
|
690
|
-
const blockSelectors = (_b = opts.block) != null ? _b : [];
|
|
691
|
-
function onInput(ev) {
|
|
156
|
+
const browser = host();
|
|
157
|
+
if (!browser || !config || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(config.key)) return;
|
|
158
|
+
if (config.audience !== "all_visitors" && config.audience !== "signed_in") return;
|
|
159
|
+
const url = new URL(config.endpoint || "https://www.sightspool.com");
|
|
160
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))) return;
|
|
161
|
+
if (url.username || url.password) return;
|
|
162
|
+
const previous = current();
|
|
163
|
+
if (previous && previous.key === config.key && previous.endpoint === url.origin && previous.audience === config.audience) return;
|
|
164
|
+
if (previous == null ? void 0 : previous.frame) return;
|
|
165
|
+
if (previous) destroy();
|
|
166
|
+
let device = "";
|
|
692
167
|
try {
|
|
693
|
-
|
|
694
|
-
if (!target || target.tagName !== "INPUT" && target.tagName !== "TEXTAREA") return;
|
|
695
|
-
if (!looksLikeSearch(target)) return;
|
|
696
|
-
if (isIgnoredElement(target, blockSelectors) || matchesRedactSelector(target, redactSelectors))
|
|
697
|
-
return;
|
|
698
|
-
const value = (target.value || "").trim();
|
|
699
|
-
if (value.length < 2) return;
|
|
700
|
-
if (timer !== null) window.clearTimeout(timer);
|
|
701
|
-
timer = window.setTimeout(() => {
|
|
702
|
-
hint = value;
|
|
703
|
-
capture.recordSearch(value);
|
|
704
|
-
if (opts.debug) console.debug("[sightspool] harvested intent", value);
|
|
705
|
-
}, DEBOUNCE_MS);
|
|
168
|
+
device = sessionStorage.getItem("sightspool-widget-device:" + config.key) || "";
|
|
706
169
|
} catch (e) {
|
|
707
170
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
if (started || typeof document === "undefined") return;
|
|
712
|
-
started = true;
|
|
171
|
+
if (!/^ss_fcd_[A-Za-z0-9_-]{43}$/.test(device)) {
|
|
172
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
173
|
+
device = "ss_fcd_" + btoa(String.fromCharCode(...bytes)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
713
174
|
try {
|
|
714
|
-
|
|
175
|
+
sessionStorage.setItem("sightspool-widget-device:" + config.key, device);
|
|
715
176
|
} catch (e) {
|
|
716
177
|
}
|
|
717
|
-
},
|
|
718
|
-
stop() {
|
|
719
|
-
started = false;
|
|
720
|
-
try {
|
|
721
|
-
document.removeEventListener("input", onInput, true);
|
|
722
|
-
if (timer !== null) window.clearTimeout(timer);
|
|
723
|
-
} catch (e) {
|
|
724
|
-
}
|
|
725
|
-
},
|
|
726
|
-
lastIntentHint() {
|
|
727
|
-
return hint;
|
|
728
178
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
void fetch(url, {
|
|
755
|
-
method: "POST",
|
|
756
|
-
headers: { "Content-Type": "text/plain" },
|
|
757
|
-
body: json,
|
|
758
|
-
keepalive: true,
|
|
759
|
-
mode: "cors",
|
|
760
|
-
credentials: "omit"
|
|
761
|
-
}).catch(() => {
|
|
762
|
-
});
|
|
763
|
-
} catch (e) {
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
function onVisibility() {
|
|
767
|
-
try {
|
|
768
|
-
if (document.visibilityState === "hidden") flush();
|
|
769
|
-
} catch (e) {
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
function flush() {
|
|
773
|
-
if (queue.length === 0) return;
|
|
774
|
-
const batch = queue;
|
|
775
|
-
queue = [];
|
|
776
|
-
send(batch);
|
|
777
|
-
}
|
|
778
|
-
return {
|
|
779
|
-
start() {
|
|
780
|
-
if (started || typeof window === "undefined") return;
|
|
781
|
-
started = true;
|
|
782
|
-
try {
|
|
783
|
-
document.addEventListener("visibilitychange", onVisibility);
|
|
784
|
-
window.addEventListener("pagehide", flush);
|
|
785
|
-
interval = window.setInterval(flush, FLUSH_INTERVAL_MS);
|
|
786
|
-
} catch (e) {
|
|
787
|
-
}
|
|
788
|
-
},
|
|
789
|
-
stop() {
|
|
790
|
-
started = false;
|
|
179
|
+
const r = {
|
|
180
|
+
key: config.key,
|
|
181
|
+
endpoint: url.origin,
|
|
182
|
+
audience: config.audience,
|
|
183
|
+
identified: false,
|
|
184
|
+
paused: false,
|
|
185
|
+
disposed: false,
|
|
186
|
+
generation: 0,
|
|
187
|
+
device,
|
|
188
|
+
offer: null,
|
|
189
|
+
button: null,
|
|
190
|
+
panel: null,
|
|
191
|
+
frame: null,
|
|
192
|
+
expanded: false,
|
|
193
|
+
message: null,
|
|
194
|
+
pending: null,
|
|
195
|
+
timer: 0,
|
|
196
|
+
visibility: () => {
|
|
197
|
+
},
|
|
198
|
+
status: "signed_out"
|
|
199
|
+
};
|
|
200
|
+
browser[slot] = r;
|
|
201
|
+
r.visibility = () => {
|
|
791
202
|
try {
|
|
792
|
-
document.
|
|
793
|
-
|
|
794
|
-
|
|
203
|
+
if (document.visibilityState === "visible") void check(r);
|
|
204
|
+
else {
|
|
205
|
+
invalidate(r);
|
|
206
|
+
status(r, r.paused ? "paused" : eligible(r) ? "unavailable" : "signed_out");
|
|
207
|
+
}
|
|
795
208
|
} catch (e) {
|
|
796
209
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
flush,
|
|
804
|
-
setIdentity(next) {
|
|
805
|
-
identity = next;
|
|
806
|
-
}
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
// src/triggers.ts
|
|
811
|
-
var RAGE_CLICK_COUNT = 3;
|
|
812
|
-
var RAGE_WINDOW_MS2 = 1e3;
|
|
813
|
-
var FRICTION_RETRY_COUNT = 3;
|
|
814
|
-
var FRICTION_WINDOW_MS = 8e3;
|
|
815
|
-
function detectFriction(trail, now, windowMs = FRICTION_WINDOW_MS) {
|
|
816
|
-
var _a2;
|
|
817
|
-
const recent = trail.filter((e) => now - e.t <= windowMs);
|
|
818
|
-
if (recent.some((e) => e.type === "error" || e.type === "request_error")) return "error";
|
|
819
|
-
const clicks = recent.filter((e) => e.type === "click" || e.type === "rage_click");
|
|
820
|
-
for (let i = 0; i < clicks.length; i++) {
|
|
821
|
-
const burst = clicks.filter(
|
|
822
|
-
(e) => e.t >= clicks[i].t && e.t - clicks[i].t <= RAGE_WINDOW_MS2
|
|
823
|
-
);
|
|
824
|
-
if (burst.length >= RAGE_CLICK_COUNT) return "rage";
|
|
825
|
-
}
|
|
826
|
-
if (recent.some((e) => e.type === "dead_click")) return "dead_end";
|
|
827
|
-
const byLabel = /* @__PURE__ */ new Map();
|
|
828
|
-
for (const e of clicks) {
|
|
829
|
-
if (!e.label) continue;
|
|
830
|
-
const n = ((_a2 = byLabel.get(e.label)) != null ? _a2 : 0) + 1;
|
|
831
|
-
byLabel.set(e.label, n);
|
|
832
|
-
if (n >= FRICTION_RETRY_COUNT) return "retry";
|
|
833
|
-
}
|
|
834
|
-
return null;
|
|
835
|
-
}
|
|
836
|
-
var DEFAULT_CAPS = {
|
|
837
|
-
maxPromptsPerSession: 1,
|
|
838
|
-
cooldownMs: 5 * 6e4
|
|
839
|
-
};
|
|
840
|
-
function canPrompt(state, now, caps = DEFAULT_CAPS) {
|
|
841
|
-
if (state.promptsShown >= caps.maxPromptsPerSession) return false;
|
|
842
|
-
if (state.lastPromptAt !== null && now - state.lastPromptAt < caps.cooldownMs) return false;
|
|
843
|
-
return true;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// src/intervention.ts
|
|
847
|
-
var RENDERABLE = /* @__PURE__ */ new Set(["survey", "demand_probe"]);
|
|
848
|
-
function str(v, max) {
|
|
849
|
-
if (typeof v !== "string") return void 0;
|
|
850
|
-
const t = v.trim();
|
|
851
|
-
return t ? t.slice(0, max) : void 0;
|
|
852
|
-
}
|
|
853
|
-
function buildServeContext(route, identity) {
|
|
854
|
-
const ctx = {};
|
|
855
|
-
const r = str(route, 500);
|
|
856
|
-
if (r) ctx.route = r;
|
|
857
|
-
const account = str(identity == null ? void 0 : identity.account, 200);
|
|
858
|
-
if (account) ctx.account = account;
|
|
859
|
-
const plan = str(identity == null ? void 0 : identity.plan, 80);
|
|
860
|
-
if (plan) ctx.plan = plan;
|
|
861
|
-
return ctx;
|
|
862
|
-
}
|
|
863
|
-
function normalizeServed(raw) {
|
|
864
|
-
if (!raw || typeof raw !== "object") return null;
|
|
865
|
-
const r = raw;
|
|
866
|
-
const id = str(r.id, 64);
|
|
867
|
-
const type = str(r.type, 40);
|
|
868
|
-
if (!id || !type || !RENDERABLE.has(type)) return null;
|
|
869
|
-
const cfg = r.config && typeof r.config === "object" ? r.config : {};
|
|
870
|
-
if (type === "demand_probe") {
|
|
871
|
-
const label = str(cfg.label, 200);
|
|
872
|
-
const disclosure = str(cfg.disclosure, 300);
|
|
873
|
-
if (!label || !disclosure) return null;
|
|
874
|
-
const rawSlot = str(cfg.slot, 64);
|
|
875
|
-
const slot = rawSlot && /^[a-z0-9][a-z0-9_-]*$/i.test(rawSlot) ? rawSlot : void 0;
|
|
876
|
-
return { id, type, config: { label, disclosure, ...slot ? { slot } : {} } };
|
|
877
|
-
}
|
|
878
|
-
const question = str(cfg.question, 500);
|
|
879
|
-
if (!question) return null;
|
|
880
|
-
const options = Array.isArray(cfg.options) ? cfg.options.map((o) => str(o, 200)).filter((o) => !!o).slice(0, 6) : void 0;
|
|
881
|
-
const config = {
|
|
882
|
-
question,
|
|
883
|
-
...options && options.length ? { options } : {},
|
|
884
|
-
allow_text: cfg.allow_text === true
|
|
885
|
-
};
|
|
886
|
-
return { id, type: "survey", config };
|
|
887
|
-
}
|
|
888
|
-
function pickRespondentKey(userId, persisted, sessionRef) {
|
|
889
|
-
const uid = str(userId, 200);
|
|
890
|
-
if (uid) return uid;
|
|
891
|
-
const p = str(persisted, 200);
|
|
892
|
-
if (p) return p;
|
|
893
|
-
return sessionRef;
|
|
894
|
-
}
|
|
895
|
-
var RK_STORAGE_KEY = "sightspool_rk";
|
|
896
|
-
function getRespondentKey(identity, sessionRef) {
|
|
897
|
-
const uid = str(identity == null ? void 0 : identity.userId, 200);
|
|
898
|
-
if (uid) return uid;
|
|
899
|
-
try {
|
|
900
|
-
let v = localStorage.getItem(RK_STORAGE_KEY);
|
|
901
|
-
if (!v) {
|
|
902
|
-
v = "anon-" + Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
903
|
-
localStorage.setItem(RK_STORAGE_KEY, v);
|
|
904
|
-
}
|
|
905
|
-
return pickRespondentKey(void 0, v, sessionRef);
|
|
906
|
-
} catch (e) {
|
|
907
|
-
return sessionRef;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
function serveUrl(endpoint, path) {
|
|
911
|
-
return `${endpoint.replace(/\/+$/, "")}/api/sdk/${path}`;
|
|
912
|
-
}
|
|
913
|
-
async function fetchIntervention(opts) {
|
|
914
|
-
var _a2;
|
|
915
|
-
try {
|
|
916
|
-
if (typeof fetch !== "function") return null;
|
|
917
|
-
const res = await fetch(serveUrl(opts.endpoint, "serve"), {
|
|
918
|
-
method: "POST",
|
|
919
|
-
headers: { "Content-Type": "text/plain" },
|
|
920
|
-
body: JSON.stringify({
|
|
921
|
-
key: opts.key,
|
|
922
|
-
context: opts.ctx,
|
|
923
|
-
respondent_key: opts.respondentKey
|
|
924
|
-
}),
|
|
925
|
-
mode: "cors",
|
|
926
|
-
credentials: "omit"
|
|
927
|
-
});
|
|
928
|
-
if (!res.ok) return null;
|
|
929
|
-
const data = await res.json();
|
|
930
|
-
const served = normalizeServed(data == null ? void 0 : data.intervention);
|
|
931
|
-
if (opts.debug) console.debug("[sightspool] serve", (_a2 = served == null ? void 0 : served.id) != null ? _a2 : "(none)");
|
|
932
|
-
return served;
|
|
933
|
-
} catch (e) {
|
|
934
|
-
return null;
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
function submitResponse(opts) {
|
|
938
|
-
try {
|
|
939
|
-
const url = serveUrl(opts.endpoint, "respond");
|
|
940
|
-
const json = JSON.stringify({
|
|
941
|
-
key: opts.key,
|
|
942
|
-
intervention_id: opts.interventionId,
|
|
943
|
-
response: opts.response,
|
|
944
|
-
respondent_key: opts.respondentKey
|
|
945
|
-
});
|
|
946
|
-
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
947
|
-
const blob = new Blob([json], { type: "text/plain" });
|
|
948
|
-
if (navigator.sendBeacon(url, blob)) return;
|
|
949
|
-
}
|
|
950
|
-
if (typeof fetch === "function") {
|
|
951
|
-
void fetch(url, {
|
|
952
|
-
method: "POST",
|
|
953
|
-
headers: { "Content-Type": "text/plain" },
|
|
954
|
-
body: json,
|
|
955
|
-
keepalive: true,
|
|
956
|
-
mode: "cors",
|
|
957
|
-
credentials: "omit"
|
|
958
|
-
}).catch(() => {
|
|
959
|
-
});
|
|
960
|
-
}
|
|
961
|
-
} catch (e) {
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
// src/env.ts
|
|
966
|
-
var LOCAL_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", ""]);
|
|
967
|
-
function isLocalhost(hostname) {
|
|
968
|
-
const h = (hostname != null ? hostname : "").trim().toLowerCase();
|
|
969
|
-
if (LOCAL_HOSTS.has(h)) return true;
|
|
970
|
-
return h.endsWith(".local") || h.endsWith(".localhost");
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
// src/index.ts
|
|
974
|
-
var DEFAULT_ENDPOINT = "https://www.sightspool.com";
|
|
975
|
-
var FRICTION_EMIT_COOLDOWN_MS = 3e4;
|
|
976
|
-
var EVENT_DEBOUNCE_MS = 600;
|
|
977
|
-
var SERVE_SETTLE_MS = 2500;
|
|
978
|
-
var MAX_INTERVENTIONS_PER_SESSION = 1;
|
|
979
|
-
function currentHostname() {
|
|
980
|
-
try {
|
|
981
|
-
return typeof location !== "undefined" ? location.hostname : "";
|
|
210
|
+
};
|
|
211
|
+
document.addEventListener("visibilitychange", r.visibility);
|
|
212
|
+
r.timer = window.setInterval(() => {
|
|
213
|
+
void check(r);
|
|
214
|
+
}, 15e3);
|
|
215
|
+
if (eligible(r)) void check(r);
|
|
982
216
|
} catch (e) {
|
|
983
|
-
return "";
|
|
984
217
|
}
|
|
985
218
|
}
|
|
986
|
-
|
|
987
|
-
function uuid() {
|
|
219
|
+
function identify(userId) {
|
|
988
220
|
try {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
}
|
|
996
|
-
function deriveCandidates(c) {
|
|
997
|
-
const out = [];
|
|
998
|
-
const hint = c.harvest.lastIntentHint();
|
|
999
|
-
if (hint) out.push(`Searching for \u201C${hint}\u201D`);
|
|
1000
|
-
const label = c.capture.label();
|
|
1001
|
-
if (label) out.push(`Something on \u201C${label.slice(0, 40)}\u201D`);
|
|
1002
|
-
return out;
|
|
1003
|
-
}
|
|
1004
|
-
function buildSignal(c, trigger, extra = {}) {
|
|
1005
|
-
const err = c.capture.error();
|
|
1006
|
-
return {
|
|
1007
|
-
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1008
|
-
route: c.capture.route(),
|
|
1009
|
-
client_label: c.capture.label(),
|
|
1010
|
-
trail: c.capture.trail(),
|
|
1011
|
-
session_ref: c.sessionRef,
|
|
1012
|
-
trigger,
|
|
1013
|
-
intent_hint: c.harvest.lastIntentHint(),
|
|
1014
|
-
error: err,
|
|
1015
|
-
outcome: err || trigger === "friction" ? "failed" : "unknown",
|
|
1016
|
-
...extra
|
|
1017
|
-
};
|
|
1018
|
-
}
|
|
1019
|
-
async function maybePrompt(c, trigger) {
|
|
1020
|
-
var _a2;
|
|
1021
|
-
const now = Date.now();
|
|
1022
|
-
if (canPrompt(c.fatigue, now, DEFAULT_CAPS)) {
|
|
1023
|
-
c.fatigue.promptsShown += 1;
|
|
1024
|
-
c.fatigue.lastPromptAt = now;
|
|
1025
|
-
try {
|
|
1026
|
-
const { showPrompt: showPrompt2 } = await Promise.resolve().then(() => (init_prompt(), prompt_exports));
|
|
1027
|
-
const result = await showPrompt2({ candidates: deriveCandidates(c) });
|
|
1028
|
-
c.egress.enqueue(
|
|
1029
|
-
buildSignal(c, trigger, {
|
|
1030
|
-
answer: result.answer,
|
|
1031
|
-
verbatim: result.verbatim,
|
|
1032
|
-
intent_hint: (_a2 = result.intent) != null ? _a2 : c.harvest.lastIntentHint(),
|
|
1033
|
-
outcome: result.answer === "yes" ? "achieved" : "failed"
|
|
1034
|
-
})
|
|
1035
|
-
);
|
|
1036
|
-
} catch (e) {
|
|
1037
|
-
c.egress.enqueue(buildSignal(c, trigger));
|
|
1038
|
-
}
|
|
1039
|
-
} else {
|
|
1040
|
-
c.egress.enqueue(buildSignal(c, trigger));
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
async function maybeServeIntervention(c) {
|
|
1044
|
-
if (!c.running || !c.interventionsEnabled || c.suppressed) return;
|
|
1045
|
-
if (c.surveyOnScreen) return;
|
|
1046
|
-
if (c.interventionsShown >= MAX_INTERVENTIONS_PER_SESSION) return;
|
|
1047
|
-
const now = Date.now();
|
|
1048
|
-
if (c.fatigue.lastPromptAt !== null && now - c.fatigue.lastPromptAt < DEFAULT_CAPS.cooldownMs)
|
|
1049
|
-
return;
|
|
1050
|
-
const respondentKey = getRespondentKey(c.identity, c.sessionRef);
|
|
1051
|
-
const served = await fetchIntervention({
|
|
1052
|
-
endpoint: c.config.endpoint,
|
|
1053
|
-
key: c.config.key,
|
|
1054
|
-
ctx: buildServeContext(c.capture.route(), c.identity),
|
|
1055
|
-
respondentKey,
|
|
1056
|
-
debug: c.config.debug
|
|
1057
|
-
});
|
|
1058
|
-
if (!served || !c.running) return;
|
|
1059
|
-
const prevShown = c.interventionsShown;
|
|
1060
|
-
const prevPromptAt = c.fatigue.lastPromptAt;
|
|
1061
|
-
c.surveyOnScreen = true;
|
|
1062
|
-
c.interventionsShown += 1;
|
|
1063
|
-
c.fatigue.lastPromptAt = Date.now();
|
|
1064
|
-
try {
|
|
1065
|
-
if (served.type === "demand_probe") {
|
|
1066
|
-
const { showProbe: showProbe2 } = await Promise.resolve().then(() => (init_probe(), probe_exports));
|
|
1067
|
-
const result = await showProbe2(served.config);
|
|
1068
|
-
if (!result.mounted) {
|
|
1069
|
-
c.interventionsShown = prevShown;
|
|
1070
|
-
c.fatigue.lastPromptAt = prevPromptAt;
|
|
1071
|
-
} else if (result.voted) {
|
|
1072
|
-
submitResponse({
|
|
1073
|
-
endpoint: c.config.endpoint,
|
|
1074
|
-
key: c.config.key,
|
|
1075
|
-
interventionId: served.id,
|
|
1076
|
-
response: { choice: "vote" },
|
|
1077
|
-
respondentKey
|
|
1078
|
-
});
|
|
1079
|
-
}
|
|
1080
|
-
} else {
|
|
1081
|
-
const { showSurvey: showSurvey2 } = await Promise.resolve().then(() => (init_survey(), survey_exports));
|
|
1082
|
-
const result = await showSurvey2(served.config);
|
|
1083
|
-
if (!result.dismissed && (result.choice || result.text)) {
|
|
1084
|
-
submitResponse({
|
|
1085
|
-
endpoint: c.config.endpoint,
|
|
1086
|
-
key: c.config.key,
|
|
1087
|
-
interventionId: served.id,
|
|
1088
|
-
response: { choice: result.choice, text: result.text },
|
|
1089
|
-
respondentKey
|
|
1090
|
-
});
|
|
1091
|
-
}
|
|
221
|
+
const r = current();
|
|
222
|
+
if (!r) return;
|
|
223
|
+
const identified = typeof userId === "string" && userId.trim().length > 0;
|
|
224
|
+
if (!identified && !r.identified) {
|
|
225
|
+
if (!r.paused && eligible(r)) void check(r);
|
|
226
|
+
return;
|
|
1092
227
|
}
|
|
228
|
+
invalidate(r);
|
|
229
|
+
r.identified = identified;
|
|
230
|
+
status(r, r.paused ? "paused" : eligible(r) ? "unavailable" : "signed_out");
|
|
231
|
+
if (eligible(r) && !r.paused) void check(r);
|
|
1093
232
|
} catch (e) {
|
|
1094
|
-
} finally {
|
|
1095
|
-
c.surveyOnScreen = false;
|
|
1096
233
|
}
|
|
1097
234
|
}
|
|
1098
|
-
function
|
|
1099
|
-
const c = ctrl;
|
|
1100
|
-
if (!c || !c.running) return;
|
|
235
|
+
function pause() {
|
|
1101
236
|
try {
|
|
1102
|
-
const
|
|
1103
|
-
if (
|
|
1104
|
-
|
|
1105
|
-
|
|
237
|
+
const r = current();
|
|
238
|
+
if (r) {
|
|
239
|
+
r.paused = true;
|
|
240
|
+
invalidate(r);
|
|
241
|
+
status(r, "paused");
|
|
1106
242
|
}
|
|
1107
243
|
} catch (e) {
|
|
1108
244
|
}
|
|
1109
|
-
if (c.eventTimer !== null) return;
|
|
1110
|
-
c.eventTimer = (typeof window !== "undefined" ? window.setTimeout : setTimeout)(() => {
|
|
1111
|
-
c.eventTimer = null;
|
|
1112
|
-
try {
|
|
1113
|
-
const now = Date.now();
|
|
1114
|
-
if (now - c.lastFrictionEmitAt < FRICTION_EMIT_COOLDOWN_MS) return;
|
|
1115
|
-
const kind = detectFriction(c.capture.trail(), now);
|
|
1116
|
-
if (!kind) return;
|
|
1117
|
-
c.lastFrictionEmitAt = now;
|
|
1118
|
-
void maybePrompt(c, "friction");
|
|
1119
|
-
} catch (e) {
|
|
1120
|
-
}
|
|
1121
|
-
}, EVENT_DEBOUNCE_MS);
|
|
1122
|
-
}
|
|
1123
|
-
function onExitIntent(ev) {
|
|
1124
|
-
const c = ctrl;
|
|
1125
|
-
if (!c || !c.running || !c.config.boundaryAsk) return;
|
|
1126
|
-
try {
|
|
1127
|
-
if (ev.clientY <= 0) void maybePrompt(c, "boundary");
|
|
1128
|
-
} catch (e) {
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
function startRuntime(c) {
|
|
1132
|
-
if (c.running) return;
|
|
1133
|
-
if (c.suppressed) {
|
|
1134
|
-
if (c.config.debug)
|
|
1135
|
-
console.debug(
|
|
1136
|
-
"[sightspool] capture suppressed on localhost (set captureOnLocalhost:true to override)"
|
|
1137
|
-
);
|
|
1138
|
-
return;
|
|
1139
|
-
}
|
|
1140
|
-
c.running = true;
|
|
1141
|
-
c.capture.start();
|
|
1142
|
-
c.harvest.start();
|
|
1143
|
-
c.egress.start();
|
|
1144
|
-
c.capture.onEvent(onCaptureEvent);
|
|
1145
|
-
try {
|
|
1146
|
-
document.addEventListener("mouseout", onExitIntent);
|
|
1147
|
-
} catch (e) {
|
|
1148
|
-
}
|
|
1149
|
-
if (c.interventionsEnabled) {
|
|
1150
|
-
c.lastServeRoute = (() => {
|
|
1151
|
-
try {
|
|
1152
|
-
return c.capture.route();
|
|
1153
|
-
} catch (e) {
|
|
1154
|
-
return null;
|
|
1155
|
-
}
|
|
1156
|
-
})();
|
|
1157
|
-
(typeof window !== "undefined" ? window.setTimeout : setTimeout)(() => {
|
|
1158
|
-
void maybeServeIntervention(c);
|
|
1159
|
-
}, SERVE_SETTLE_MS);
|
|
1160
|
-
}
|
|
1161
|
-
if (c.config.debug) console.debug("[sightspool] started", c.sessionRef);
|
|
1162
245
|
}
|
|
1163
|
-
function
|
|
246
|
+
function resume() {
|
|
1164
247
|
try {
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
const endpoint = config.endpoint || DEFAULT_ENDPOINT;
|
|
1171
|
-
const privacyOpts = { redact: config.redact, block: config.block };
|
|
1172
|
-
const capture = createCapture({ debug: !!config.debug, ...privacyOpts });
|
|
1173
|
-
const harvest = createHarvest(capture, { debug: !!config.debug, ...privacyOpts });
|
|
1174
|
-
const egress = createEgress({ key: config.key, endpoint, debug: !!config.debug });
|
|
1175
|
-
ctrl = {
|
|
1176
|
-
config: {
|
|
1177
|
-
...config,
|
|
1178
|
-
key: config.key,
|
|
1179
|
-
endpoint,
|
|
1180
|
-
boundaryAsk: config.boundaryAsk !== false,
|
|
1181
|
-
debug: !!config.debug
|
|
1182
|
-
},
|
|
1183
|
-
capture,
|
|
1184
|
-
harvest,
|
|
1185
|
-
egress,
|
|
1186
|
-
identity: {},
|
|
1187
|
-
sessionRef: uuid(),
|
|
1188
|
-
fatigue: { promptsShown: 0, lastPromptAt: null },
|
|
1189
|
-
lastFrictionEmitAt: 0,
|
|
1190
|
-
eventTimer: null,
|
|
1191
|
-
running: false,
|
|
1192
|
-
suppressed: isLocalhost(currentHostname()) && config.captureOnLocalhost !== true,
|
|
1193
|
-
interventionsEnabled: config.interventions !== false,
|
|
1194
|
-
lastServeRoute: null,
|
|
1195
|
-
surveyOnScreen: false,
|
|
1196
|
-
interventionsShown: 0
|
|
1197
|
-
};
|
|
1198
|
-
if (config.consent !== false) startRuntime(ctrl);
|
|
1199
|
-
} catch (err) {
|
|
1200
|
-
try {
|
|
1201
|
-
console.warn("[sightspool] init failed", err);
|
|
1202
|
-
} catch (e) {
|
|
248
|
+
const r = current();
|
|
249
|
+
if (r) {
|
|
250
|
+
r.paused = false;
|
|
251
|
+
if (!eligible(r)) status(r, "signed_out");
|
|
252
|
+
void check(r);
|
|
1203
253
|
}
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
function identify(userId, traits) {
|
|
1207
|
-
try {
|
|
1208
|
-
if (!ctrl) return;
|
|
1209
|
-
ctrl.identity = { userId, account: traits == null ? void 0 : traits.account, plan: traits == null ? void 0 : traits.plan };
|
|
1210
|
-
ctrl.egress.setIdentity(ctrl.identity);
|
|
1211
|
-
} catch (e) {
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
function start() {
|
|
1215
|
-
try {
|
|
1216
|
-
if (ctrl) startRuntime(ctrl);
|
|
1217
254
|
} catch (e) {
|
|
1218
255
|
}
|
|
1219
256
|
}
|
|
1220
|
-
function
|
|
257
|
+
function destroy() {
|
|
1221
258
|
try {
|
|
1222
|
-
|
|
1223
|
-
if (
|
|
1224
|
-
|
|
259
|
+
const r = current();
|
|
260
|
+
if (!r) return;
|
|
261
|
+
r.disposed = true;
|
|
262
|
+
invalidate(r);
|
|
263
|
+
window.clearInterval(r.timer);
|
|
264
|
+
document.removeEventListener("visibilitychange", r.visibility);
|
|
265
|
+
if (!r.frame) delete host()[slot];
|
|
1225
266
|
} catch (e) {
|
|
1226
267
|
}
|
|
1227
268
|
}
|
|
1228
|
-
function
|
|
269
|
+
function getStatus() {
|
|
270
|
+
var _a, _b;
|
|
1229
271
|
try {
|
|
1230
|
-
|
|
1231
|
-
ctrl.running = false;
|
|
1232
|
-
ctrl.capture.stop();
|
|
1233
|
-
ctrl.harvest.stop();
|
|
1234
|
-
ctrl.egress.stop();
|
|
1235
|
-
try {
|
|
1236
|
-
document.removeEventListener("mouseout", onExitIntent);
|
|
1237
|
-
} catch (e) {
|
|
1238
|
-
}
|
|
272
|
+
return (_b = (_a = current()) == null ? void 0 : _a.status) != null ? _b : "not_initialized";
|
|
1239
273
|
} catch (e) {
|
|
274
|
+
return "error";
|
|
1240
275
|
}
|
|
1241
276
|
}
|
|
1242
|
-
var api = { init, identify, start, stop, consent };
|
|
1243
|
-
var src_default = api;
|
|
1244
|
-
var _a;
|
|
1245
|
-
try {
|
|
1246
|
-
if (typeof document !== "undefined") {
|
|
1247
|
-
const el = (_a = document.currentScript) != null ? _a : document.querySelector("script[data-sightspool-key]");
|
|
1248
|
-
const key = el == null ? void 0 : el.getAttribute("data-sightspool-key");
|
|
1249
|
-
if (key) {
|
|
1250
|
-
let endpoint = (el == null ? void 0 : el.getAttribute("data-sightspool-endpoint")) || void 0;
|
|
1251
|
-
if (!endpoint && (el == null ? void 0 : el.src)) {
|
|
1252
|
-
try {
|
|
1253
|
-
endpoint = new URL(el.src).origin;
|
|
1254
|
-
} catch (e) {
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
const list = (attr) => {
|
|
1258
|
-
const raw = el == null ? void 0 : el.getAttribute(attr);
|
|
1259
|
-
if (!raw) return void 0;
|
|
1260
|
-
const out = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1261
|
-
return out.length ? out : void 0;
|
|
1262
|
-
};
|
|
1263
|
-
init({
|
|
1264
|
-
key,
|
|
1265
|
-
endpoint,
|
|
1266
|
-
redact: list("data-sightspool-redact"),
|
|
1267
|
-
block: list("data-sightspool-block"),
|
|
1268
|
-
captureOnLocalhost: (el == null ? void 0 : el.hasAttribute("data-sightspool-capture-localhost")) || void 0,
|
|
1269
|
-
interventions: (el == null ? void 0 : el.hasAttribute("data-sightspool-no-interventions")) ? false : void 0,
|
|
1270
|
-
debug: (el == null ? void 0 : el.hasAttribute("data-sightspool-debug")) || void 0
|
|
1271
|
-
});
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
} catch (e) {
|
|
1275
|
-
}
|
|
1276
277
|
|
|
1277
|
-
|
|
278
|
+
// src/index.ts
|
|
279
|
+
var src_default = { init, identify, pause, resume, destroy, getStatus };
|
|
280
|
+
|
|
1278
281
|
exports.default = src_default;
|
|
282
|
+
exports.destroy = destroy;
|
|
283
|
+
exports.getStatus = getStatus;
|
|
1279
284
|
exports.identify = identify;
|
|
1280
285
|
exports.init = init;
|
|
1281
|
-
exports.
|
|
1282
|
-
exports.
|
|
286
|
+
exports.pause = pause;
|
|
287
|
+
exports.resume = resume;
|
|
1283
288
|
//# sourceMappingURL=index.cjs.map
|
|
1284
289
|
//# sourceMappingURL=index.cjs.map
|