@duffcloudservices/telemetry 0.2.1 → 0.3.1

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.
@@ -0,0 +1,74 @@
1
+ import { afterEach, describe, expect, it } from 'vitest'
2
+
3
+ import { SYNTHETIC_CAPTURE_PARAMS, syntheticCaptureDetected } from './syntheticCapture'
4
+
5
+ /**
6
+ * C-614. Both directions matter and they are NOT symmetric:
7
+ *
8
+ * - Under-detecting re-creates the measured defect (49 synthetic vs 14 organic
9
+ * pageViews on coron8 over the 8 days to 2026-08-11).
10
+ * - Over-detecting silently DELETES real visits, and a missing pageView leaves
11
+ * nothing behind to notice it by. The "real visit" cases are therefore the
12
+ * load-bearing half.
13
+ */
14
+
15
+ const originalLocation = window.location
16
+
17
+ function setSearch(search: string) {
18
+ Object.defineProperty(window, 'location', {
19
+ configurable: true,
20
+ value: { hostname: 'duffcloudservices.com', pathname: '/', search } as Location,
21
+ })
22
+ }
23
+
24
+ afterEach(() => {
25
+ Object.defineProperty(window, 'location', { configurable: true, value: originalLocation })
26
+ })
27
+
28
+ describe('syntheticCaptureDetected', () => {
29
+ it.each(SYNTHETIC_CAPTURE_PARAMS)('detects the valueless %s the capture pass emits', (param) => {
30
+ setSearch(`?${param}=`)
31
+ expect(syntheticCaptureDetected()).toBe(true)
32
+ })
33
+
34
+ it('detects a marker appended after existing parameters', () => {
35
+ setSearch('?utm_source=newsletter&dcs-no-telemetry=')
36
+ expect(syntheticCaptureDetected()).toBe(true)
37
+ })
38
+
39
+ it('counts a bare visit', () => {
40
+ setSearch('')
41
+ expect(syntheticCaptureDetected()).toBe(false)
42
+ })
43
+
44
+ it('counts a campaign visit', () => {
45
+ setSearch('?utm_source=google&utm_medium=cpc&gclid=abc123')
46
+ expect(syntheticCaptureDetected()).toBe(false)
47
+ })
48
+
49
+ it('does not match a lookalike value or a substring', () => {
50
+ setSearch('?ref=dcs-hide-ribbon&note=dcs-no-telemetry-please')
51
+ expect(syntheticCaptureDetected()).toBe(false)
52
+ })
53
+
54
+ it('re-reads the URL rather than caching the first verdict', () => {
55
+ setSearch('?dcs-no-telemetry=')
56
+ expect(syntheticCaptureDetected()).toBe(true)
57
+ setSearch('')
58
+ expect(syntheticCaptureDetected()).toBe(false)
59
+ })
60
+
61
+ it('fails OPEN when the query string cannot be read', () => {
62
+ Object.defineProperty(window, 'location', {
63
+ configurable: true,
64
+ value: {
65
+ hostname: 'duffcloudservices.com',
66
+ pathname: '/',
67
+ get search(): string {
68
+ throw new Error('unreadable location')
69
+ },
70
+ } as unknown as Location,
71
+ })
72
+ expect(syntheticCaptureDetected()).toBe(false)
73
+ })
74
+ })
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Synthetic-capture detection (C-614).
3
+ *
4
+ * The post-deploy "Capture page snapshots" CI step drives real Chromium against
5
+ * the LIVE production URL of the site it just deployed, so the site's telemetry
6
+ * boots exactly as it would for a visitor and every page the capture walked is
7
+ * recorded as a production pageView — during our own deploy run. Measured on
8
+ * coron8 over the 8 days to 2026-08-11: 49 synthetic pageViews against 14
9
+ * organic, 78% of the entire store, all 49 on 08-11 across 5 deploys versus 3
10
+ * real visits that day.
11
+ *
12
+ * The capture tooling marks every URL it loads (`packages/cli/src/commands/
13
+ * capture-snapshots.ts` and each site's own `scripts/capture-snapshots.*`).
14
+ * `initialize()` reads that mark and declines, so nothing is emitted at all.
15
+ * Suppressing here rather than tagging and filtering downstream keeps the
16
+ * denominator honest in every read path at once instead of obliging each of
17
+ * them to remember an exclusion — the C-430 preview/localhost blend is the
18
+ * recorded UNSOLVED residual that shape produces.
19
+ *
20
+ * DELIBERATE DUPLICATION. `@duffcloudservices/cms` carries the same predicate in
21
+ * its own `syntheticCapture.ts`. These two packages are intentionally decoupled
22
+ * — this one is the low-level telemetry package (peer deps: the App Insights SDK
23
+ * and vue, nothing else), cms is the site-content package — and a dependency
24
+ * edge between them to share ten lines of pure string matching would cost more
25
+ * than the duplication does. The parameter list is the contract; keep the two
26
+ * lists identical, and note that the CI drift guard in the canonical
27
+ * site-deploy workflow greps for these same names.
28
+ */
29
+
30
+ /** Query parameters that mark a document as an automated capture, not a visit. */
31
+ export const SYNTHETIC_CAPTURE_PARAMS = ['dcs-hide-ribbon', 'dcs-no-telemetry'] as const
32
+
33
+ /**
34
+ * Whether this document is an automated capture rather than a visit worth counting.
35
+ *
36
+ * Reads `window.location.search` on every call rather than caching, so a
37
+ * client-side route change is judged on the CURRENT URL. Returns `false` on the
38
+ * server, and `false` when the query string cannot be read: over-suppression
39
+ * deletes real visits and leaves no trace to notice it by, so this fails OPEN.
40
+ */
41
+ export function syntheticCaptureDetected(): boolean {
42
+ if (typeof window === 'undefined') return false
43
+
44
+ try {
45
+ const params = new URLSearchParams(window.location.search)
46
+ return SYNTHETIC_CAPTURE_PARAMS.some((param) => params.has(param))
47
+ } catch {
48
+ return false
49
+ }
50
+ }