Added the "recorder" flag to the Analytics type in the Infrastructure package.

This commit is contained in:
2026-08-13 01:19:12 +02:00
parent 964e7fa707
commit 3bcae4d789
2 changed files with 56 additions and 2 deletions
@@ -4,7 +4,8 @@ import Foundation
///
/// A page carries it as an optional value so the ``Page`` scaffolding renders the tracker's deferred `<script>` in the document head, or omits it
/// when the page has none. The attribute names follow the Umami tracker convention. The three behavior flags default to on, and each renders its
/// `data-` attribute only when enabled, since the tracker treats an absent attribute as off.
/// `data-` attribute only when enabled, since the tracker treats an absent attribute as off. Recorder mode, by contrast, defaults to off; when
/// activated, the page embeds a second deferred script that loads the session recorder from the tracker's origin.
public struct Analytics: Sendable {
// MARK: Type aliases
@@ -25,6 +26,9 @@ public struct Analytics: Sendable {
/// Whether the tracker collects Core Web Vitals from visitors (requires an Umami instance at v3.1 or newer).
public let performance: Bool
/// Whether the tracker also records visitor sessions, loading the session recorder script alongside the tracker.
public let recorder: Bool
/// The URL the tracker script is loaded from.
public let scriptURL: String
@@ -41,13 +45,15 @@ public struct Analytics: Sendable {
/// - excludeHash: whether the tracker drops the URL fragment from reported pageviews; defaults to `true`.
/// - doNotTrack: whether the tracker honors the visitor's browser Do Not Track preference; defaults to `true`.
/// - performance: whether the tracker collects Core Web Vitals (requires Umami v3.1 or newer); defaults to `true`.
/// - recorder: whether the tracker also records visitor sessions, loading the session recorder script alongside the tracker; defaults to `false`.
public init(
scriptURL: String,
websiteID: String,
domains: String,
excludeHash: Bool = true,
doNotTrack: Bool = true,
performance: Bool = true
performance: Bool = true,
recorder: Bool = false
) {
self.scriptURL = scriptURL
self.websiteID = websiteID
@@ -55,6 +61,7 @@ public struct Analytics: Sendable {
self.excludeHash = excludeHash
self.doNotTrack = doNotTrack
self.performance = performance
self.recorder = recorder
}
// MARK: Computed
@@ -114,4 +121,16 @@ public struct Analytics: Sendable {
return "\(scheme)://\(host)\(port)"
}
/// The URL the session recorder script is loaded from, derived from the tracker's ``origin``, or `nil` when ``recorder`` mode is off
/// or no origin can be derived from the ``scriptURL``.
///
/// A page renders it as a second deferred script tag after the tracker script, carrying only the `data-website-id` attribute.
public var recorderScriptURL: String? {
guard recorder, let origin else {
return nil
}
return "\(origin)/recorder.js"
}
}
@@ -67,6 +67,41 @@ struct AnalyticsTests {
#expect(analytics.origin == nil)
}
@Test
func `carries no recorder script URL by default`() {
let analytics = Analytics(
scriptURL: "https://analytics.example.com/script",
websiteID: "id-123",
domains: "example.com"
)
#expect(analytics.recorderScriptURL == nil)
}
@Test
func `derives its recorder script URL from the origin when recorder mode is on`() {
let analytics = Analytics(
scriptURL: "https://analytics.example.com/script",
websiteID: "id-123",
domains: "example.com",
recorder: true
)
#expect(analytics.recorderScriptURL == "https://analytics.example.com/recorder.js")
}
@Test
func `carries no recorder script URL when no origin can be derived`() {
let analytics = Analytics(
scriptURL: "/script",
websiteID: "id-123",
domains: "example.com",
recorder: true
)
#expect(analytics.recorderScriptURL == nil)
}
@Test
func `omits the disabled behavior flags`() {
let analytics = Analytics(