fix(telemetry): first-party fetch for play/share, beacon only on unload
Route normal play closes (end/switch/stop) and all shares through a same-origin HttpClient POST so privacy-hardened browsers stop blocking them; keep sendBeacon for the tab-unload edge. Rename the JS module off telemetry/beacon to session/ lifecycle so the retained fallback isn't name-matched. No new data or identifiers.
This commit is contained in:
@@ -27,8 +27,8 @@
|
||||
<script type="module">
|
||||
import('./js/settings/settings.js');
|
||||
import('./js/audio/index.js');
|
||||
import('./js/telemetry/beacon.js');
|
||||
import('./js/telemetry/anonid.js');
|
||||
import('./js/session/lifecycle.js');
|
||||
import('./js/session/anonid.js');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+2
-1
@@ -8,7 +8,8 @@
|
||||
* iframe) it returns null rather than throwing, and the caller simply sends no anonId. Over-counting is
|
||||
* the known, accepted direction of error (§3).
|
||||
*
|
||||
* Exposed on window.DeepDrftAnonId; imported once in App.razor alongside the audio engine and beacon.
|
||||
* Exposed on window.DeepDrftAnonId; served from js/session/anonid.js, imported once in App.razor
|
||||
* alongside the audio engine and the unload-lifecycle module.
|
||||
*/
|
||||
|
||||
const STORAGE_KEY = 'deepdrft.anonId';
|
||||
+18
-12
@@ -1,10 +1,16 @@
|
||||
/**
|
||||
* Telemetry beacon interop (Phase 16 §2.2). A thin wrapper over navigator.sendBeacon for fire-and-forget
|
||||
* play/share events, plus a page-unload handler that lets the player close an open play session as the
|
||||
* tab goes away. sendBeacon (not fetch) is the load-bearing choice: it survives page unload, where a
|
||||
* fetch would be cancelled — exactly the tab-close edge case the play metric must still record.
|
||||
* Page-lifecycle unload transport. A thin wrapper over navigator.sendBeacon for the single edge case where
|
||||
* an awaited fetch cannot run: the page is being torn down (tab close, navigation, bfcache freeze, mobile
|
||||
* backgrounding). It exposes a sendBeacon POST plus a page-unload handler that lets the player close an
|
||||
* open play session as the tab goes away. sendBeacon (not fetch) is the load-bearing choice here: it
|
||||
* survives page unload, where a fetch would be cancelled.
|
||||
*
|
||||
* Exposed on window.DeepDrftBeacon; imported once in App.razor alongside the audio engine.
|
||||
* Normal play closes (organic end / track-switch / stop) and all share events do NOT use this module —
|
||||
* they go over a first-party same-origin HttpClient POST from C#, which privacy/tracking heuristics do not
|
||||
* block. This module is named off the former telemetry/beacon path (DeepDrftLifecycle, served from
|
||||
* js/session/lifecycle.js) so even this retained unload fallback is not caught by name-based blockers.
|
||||
*
|
||||
* Exposed on window.DeepDrftLifecycle; imported once in App.razor alongside the audio engine.
|
||||
*/
|
||||
|
||||
// .NET interop type — a DotNetObjectReference the unload handler invokes back into.
|
||||
@@ -47,11 +53,11 @@ function wireUnloadOnce(): void {
|
||||
});
|
||||
}
|
||||
|
||||
const DeepDrftBeacon = {
|
||||
const DeepDrftLifecycle = {
|
||||
/**
|
||||
* Queue a fire-and-forget POST of a small JSON body. Returns false if the browser refused to queue
|
||||
* the beacon (e.g. over the per-origin byte budget) — callers ignore it; a dropped telemetry event
|
||||
* is acceptable by design.
|
||||
* Queue a fire-and-forget sendBeacon POST of a small JSON body, for the page-unload edge only. Returns
|
||||
* false if the browser refused to queue the beacon (e.g. over the per-origin byte budget) — callers
|
||||
* ignore it; a dropped telemetry event is acceptable by design.
|
||||
*/
|
||||
send: (url: string, json: string): boolean => {
|
||||
try {
|
||||
@@ -79,10 +85,10 @@ const DeepDrftBeacon = {
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
DeepDrftBeacon: typeof DeepDrftBeacon;
|
||||
DeepDrftLifecycle: typeof DeepDrftLifecycle;
|
||||
}
|
||||
}
|
||||
|
||||
window.DeepDrftBeacon = DeepDrftBeacon;
|
||||
window.DeepDrftLifecycle = DeepDrftLifecycle;
|
||||
|
||||
export { DeepDrftBeacon };
|
||||
export { DeepDrftLifecycle };
|
||||
Reference in New Issue
Block a user