@affiliateo/web 4.9.1 → 4.9.3
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 +6 -0
- package/dist/queue.d.ts +6 -6
- package/dist/queue.js +39 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -270,6 +270,12 @@ the script tag, `data-chat-color`, `data-chat-icon-color`, `data-chat-send-color
|
|
|
270
270
|
|
|
271
271
|
## Changelog
|
|
272
272
|
|
|
273
|
+
- **4.9.3:** the events still waiting when a page closes now reach Affiliateo
|
|
274
|
+
on websites with strict privacy settings too (a referrer policy of
|
|
275
|
+
`no-referrer` or `same-origin`, the defaults of Helmet and Django). Nothing
|
|
276
|
+
to change in your code.
|
|
277
|
+
- **4.9.2:** events are sent as plain text, so the browser no longer makes an
|
|
278
|
+
extra check request before each one. Nothing to change in your code.
|
|
273
279
|
- **4.9.1:** a different user signing in with `identify()` (no `logout()` in
|
|
274
280
|
between) now forgets the last person's Live Chat thread first, as a sign-out
|
|
275
281
|
does, and the bubble learns the new user without a reload. Signing in from
|
package/dist/queue.d.ts
CHANGED
|
@@ -84,15 +84,15 @@ export declare class EventQueue {
|
|
|
84
84
|
* burst path + the timer firing simultaneously) only run one
|
|
85
85
|
* flush at a time. */
|
|
86
86
|
flush(): Promise<void>;
|
|
87
|
-
/** Best-effort sync flush triggered on page unload
|
|
88
|
-
*
|
|
89
|
-
* the last chance.
|
|
87
|
+
/** Best-effort sync flush triggered on page unload: each event goes out
|
|
88
|
+
* in a way that survives the tab closing (sendAtUnload). No retry - this
|
|
89
|
+
* is the last chance.
|
|
90
90
|
*
|
|
91
91
|
* Called from the provider's pagehide / visibilitychange=hidden
|
|
92
92
|
* handlers so any events still queued at unload get one last shot.
|
|
93
|
-
* After this call, the in-memory queue is cleared (events
|
|
94
|
-
*
|
|
95
|
-
*
|
|
93
|
+
* After this call, the in-memory queue is cleared (events the browser
|
|
94
|
+
* couldn't take are lost - that's the cost of the guaranteed-fast
|
|
95
|
+
* unload path). */
|
|
96
96
|
flushOnUnload(): void;
|
|
97
97
|
/** Remove the queued events `predicate` picks (4.8.0). A sign-in uses it
|
|
98
98
|
* to drop a sign-out still waiting to be delivered: the sign-in replaces
|
package/dist/queue.js
CHANGED
|
@@ -46,6 +46,36 @@ const MAX_RETRIES = 3;
|
|
|
46
46
|
const DEFAULT_MAX_QUEUE_SIZE = 100;
|
|
47
47
|
const DEFAULT_FLUSH_INTERVAL_MS = 5000;
|
|
48
48
|
const SIZE_FLUSH_THRESHOLD = 10;
|
|
49
|
+
// Plain text, not application/json: a JSON body makes the browser send an
|
|
50
|
+
// extra OPTIONS request before every event. The server reads the body as
|
|
51
|
+
// text and parses it itself, so nothing else changes.
|
|
52
|
+
const BODY_TYPE = 'text/plain;charset=UTF-8';
|
|
53
|
+
/** Whether a fetch here outlives the page (keepalive; Firefox only since 133). */
|
|
54
|
+
const KEEPALIVE_FETCH = typeof Request === 'function' && 'keepalive' in Request.prototype;
|
|
55
|
+
/**
|
|
56
|
+
* One last send as the page goes away. A fetch in CORS mode with keepalive,
|
|
57
|
+
* not sendBeacon: a plain-text beacon goes out as no-cors, and on a site
|
|
58
|
+
* whose referrer policy is no-referrer or same-origin (Helmet's and Django's
|
|
59
|
+
* defaults) the browser then sends "Origin: null", which the server refuses,
|
|
60
|
+
* since it takes events only from the app's own site. A CORS fetch always
|
|
61
|
+
* carries the real Origin, is still a simple request (no OPTIONS), and
|
|
62
|
+
* keepalive lets it outlive the page the way a beacon does. Browsers without
|
|
63
|
+
* keepalive keep the beacon (4.9.3). Same in t.js (public/t.js, post()).
|
|
64
|
+
*/
|
|
65
|
+
function sendAtUnload(endpoint, body) {
|
|
66
|
+
try {
|
|
67
|
+
if (KEEPALIVE_FETCH) {
|
|
68
|
+
fetch(endpoint, { method: 'POST', mode: 'cors', credentials: 'omit', headers: { 'Content-Type': BODY_TYPE }, body, keepalive: true }).catch(() => { });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (typeof navigator !== 'undefined' && navigator.sendBeacon && navigator.sendBeacon(endpoint, new Blob([body], { type: BODY_TYPE })))
|
|
72
|
+
return;
|
|
73
|
+
fetch(endpoint, { method: 'POST', headers: { 'Content-Type': BODY_TYPE }, body, keepalive: true }).catch(() => { });
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// The last chance: nothing left to do.
|
|
77
|
+
}
|
|
78
|
+
}
|
|
49
79
|
function lsGet(key) {
|
|
50
80
|
try {
|
|
51
81
|
return localStorage.getItem(key);
|
|
@@ -188,7 +218,7 @@ export class EventQueue {
|
|
|
188
218
|
try {
|
|
189
219
|
const res = await fetch(event.endpoint, {
|
|
190
220
|
method: 'POST',
|
|
191
|
-
headers: { 'Content-Type':
|
|
221
|
+
headers: { 'Content-Type': BODY_TYPE },
|
|
192
222
|
body: JSON.stringify(event.payload),
|
|
193
223
|
// keepalive lets the request survive a navigation. paired
|
|
194
224
|
// with the queue so even if this fetch errors, we'll retry.
|
|
@@ -223,43 +253,20 @@ export class EventQueue {
|
|
|
223
253
|
this.persist();
|
|
224
254
|
this.isFlushing = false;
|
|
225
255
|
}
|
|
226
|
-
/** Best-effort sync flush triggered on page unload
|
|
227
|
-
*
|
|
228
|
-
* the last chance.
|
|
256
|
+
/** Best-effort sync flush triggered on page unload: each event goes out
|
|
257
|
+
* in a way that survives the tab closing (sendAtUnload). No retry - this
|
|
258
|
+
* is the last chance.
|
|
229
259
|
*
|
|
230
260
|
* Called from the provider's pagehide / visibilitychange=hidden
|
|
231
261
|
* handlers so any events still queued at unload get one last shot.
|
|
232
|
-
* After this call, the in-memory queue is cleared (events
|
|
233
|
-
*
|
|
234
|
-
*
|
|
262
|
+
* After this call, the in-memory queue is cleared (events the browser
|
|
263
|
+
* couldn't take are lost - that's the cost of the guaranteed-fast
|
|
264
|
+
* unload path). */
|
|
235
265
|
flushOnUnload() {
|
|
236
266
|
if (this.queue.length === 0)
|
|
237
267
|
return;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
// keepalive as a fallback. Won't actually finish, but it's our
|
|
241
|
-
// last shot.
|
|
242
|
-
for (const event of this.queue) {
|
|
243
|
-
try {
|
|
244
|
-
fetch(event.endpoint, {
|
|
245
|
-
method: 'POST',
|
|
246
|
-
headers: { 'Content-Type': 'application/json' },
|
|
247
|
-
body: JSON.stringify(event.payload),
|
|
248
|
-
keepalive: true,
|
|
249
|
-
}).catch(() => { });
|
|
250
|
-
}
|
|
251
|
-
catch { /* swallow */ }
|
|
252
|
-
}
|
|
253
|
-
this.queue = [];
|
|
254
|
-
this.persist();
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
for (const event of this.queue) {
|
|
258
|
-
try {
|
|
259
|
-
navigator.sendBeacon(event.endpoint, new Blob([JSON.stringify(event.payload)], { type: 'application/json' }));
|
|
260
|
-
}
|
|
261
|
-
catch { /* swallow */ }
|
|
262
|
-
}
|
|
268
|
+
for (const event of this.queue)
|
|
269
|
+
sendAtUnload(event.endpoint, JSON.stringify(event.payload));
|
|
263
270
|
this.queue = [];
|
|
264
271
|
this.persist();
|
|
265
272
|
}
|