@affiliateo/web 4.9.2 → 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 CHANGED
@@ -270,6 +270,10 @@ 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.
273
277
  - **4.9.2:** events are sent as plain text, so the browser no longer makes an
274
278
  extra check request before each one. Nothing to change in your code.
275
279
  - **4.9.1:** a different user signing in with `identify()` (no `logout()` in
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. Uses sendBeacon
88
- * for each event so it survives the tab closing. No retry - this is
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 that
94
- * sendBeacon couldn't queue are lost - that's the cost of the
95
- * guaranteed-fast unload path). */
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
@@ -50,6 +50,32 @@ const SIZE_FLUSH_THRESHOLD = 10;
50
50
  // extra OPTIONS request before every event. The server reads the body as
51
51
  // text and parses it itself, so nothing else changes.
52
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
+ }
53
79
  function lsGet(key) {
54
80
  try {
55
81
  return localStorage.getItem(key);
@@ -227,43 +253,20 @@ export class EventQueue {
227
253
  this.persist();
228
254
  this.isFlushing = false;
229
255
  }
230
- /** Best-effort sync flush triggered on page unload. Uses sendBeacon
231
- * for each event so it survives the tab closing. No retry - this is
232
- * 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.
233
259
  *
234
260
  * Called from the provider's pagehide / visibilitychange=hidden
235
261
  * handlers so any events still queued at unload get one last shot.
236
- * After this call, the in-memory queue is cleared (events that
237
- * sendBeacon couldn't queue are lost - that's the cost of the
238
- * guaranteed-fast unload path). */
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). */
239
265
  flushOnUnload() {
240
266
  if (this.queue.length === 0)
241
267
  return;
242
- if (typeof navigator === 'undefined' || !navigator.sendBeacon) {
243
- // No sendBeacon (very old browser) - try a sync fetch with
244
- // keepalive as a fallback. Won't actually finish, but it's our
245
- // last shot.
246
- for (const event of this.queue) {
247
- try {
248
- fetch(event.endpoint, {
249
- method: 'POST',
250
- headers: { 'Content-Type': BODY_TYPE },
251
- body: JSON.stringify(event.payload),
252
- keepalive: true,
253
- }).catch(() => { });
254
- }
255
- catch { /* swallow */ }
256
- }
257
- this.queue = [];
258
- this.persist();
259
- return;
260
- }
261
- for (const event of this.queue) {
262
- try {
263
- navigator.sendBeacon(event.endpoint, new Blob([JSON.stringify(event.payload)], { type: BODY_TYPE }));
264
- }
265
- catch { /* swallow */ }
266
- }
268
+ for (const event of this.queue)
269
+ sendAtUnload(event.endpoint, JSON.stringify(event.payload));
267
270
  this.queue = [];
268
271
  this.persist();
269
272
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@affiliateo/web",
3
- "version": "4.9.2",
3
+ "version": "4.9.3",
4
4
  "description": "Affiliateo web SDK: affiliate attribution and tracking for web apps",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",