Files
deepdrft/DeepDrftPublic.Client/Services/IPlayEventSink.cs
T
daniel-c-harvey 2af0d8650b 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.
2026-06-26 21:11:43 -04:00

35 lines
1.8 KiB
C#

using DeepDrftModels.Enums;
namespace DeepDrftPublic.Client.Services;
/// <summary>
/// The emit seam for the <see cref="PlayTracker"/> (Phase 16 §2.1). The tracker owns the session
/// lifecycle, the engagement floor, and the bucket classification but knows nothing about transport —
/// it hands a finished classification to a sink, choosing only which arm fits the close that triggered
/// it. Two arms exist because the close paths differ in whether the page survives the call (telemetry
/// transport-resilience):
/// <list type="bullet">
/// <item><see cref="EmitPlayAsync"/> — normal closes (organic end / track-switch / stop), where the page
/// stays alive, go over a first-party <c>HttpClient</c> POST to <c>api/event/play</c>. A first-party
/// fetch is not name-matched by tracking/fingerprinting heuristics the way a <c>sendBeacon</c> module is.</item>
/// <item><see cref="EmitPlayOnUnload"/> — the page-unload edge (pagehide / visibility→hidden), where an
/// awaited fetch would be cancelled, still goes over <c>navigator.sendBeacon</c>.</item>
/// </list>
/// Tests substitute a fake sink to assert floor and bucket behaviour with no transport.
/// </summary>
public interface IPlayEventSink
{
/// <summary>
/// Emit one recorded play over the first-party fetch transport (normal close: end / switch / stop).
/// Called at most once per session, only when the floor is crossed. Awaitable but safe to
/// fire-and-forget on a live page; never throws.
/// </summary>
Task EmitPlayAsync(string trackEntryKey, PlayBucket bucket);
/// <summary>
/// Emit one recorded play over <c>sendBeacon</c> for the page-unload edge, where an awaited fetch
/// would be cancelled as the page freezes. Synchronous and fire-and-forget; never throws.
/// </summary>
void EmitPlayOnUnload(string trackEntryKey, PlayBucket bucket);
}