@duffcloudservices/telemetry 0.2.1 → 0.3.0

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/composable.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { ApplicationInsights } from '@microsoft/applicationinsights-web'\nimport { computed, ref } from 'vue'\n\nimport { DEFAULT_CLOUD_ROLE, createTelemetryConfig } from './config'\nimport { captureJourneyFirstTouch, setJourneyIdentityResolver } from './journey'\nimport type {\n TelemetryDependency,\n TelemetryEvent,\n TelemetryException,\n TelemetryMetric,\n TelemetryOptions,\n TelemetryPageView,\n} from './types'\n\ninterface TelemetryEnvelope {\n data?: {\n baseData?: {\n properties?: Record<string, string>\n }\n }\n tags?: Record<string, string>\n}\n\n// Module-level singleton state: one App Insights instance per app bundle,\n// shared across every `useTelemetry()` call (matches the original per-app\n// composable). The reactive refs let views observe init state.\nlet appInsights: ApplicationInsights | null = null\nconst isInitialized = ref(false)\nconst initializationError = ref<string | null>(null)\n\n// One-shot guards so we surface \"App Insights is not configured\" loudly in\n// DevTools the FIRST time the app tries to use it, but don't spam the console\n// for every subsequent trackEvent / trackPageView call. See web/ site-audit F2\n// — `customEvents = 0` for 7d was a missing connection string silently\n// no-op'ing every telemetry call; the loud warning makes that mode visible.\nlet warnedDisabled = false\nlet warnedNotInitialized = false\nlet warnedDoubleCount = false\n\n// Whether the live instance was created with the SDK's own SPA route tracking on.\n// When it is, the SDK already emits one pageView per navigation, so a consumer that\n// ALSO calls trackPageView() on route change counts every page twice.\n//\n// This is not hypothetical (C-288, 2026-07-25): the Just Posh site ran both at once\n// and 509 of its 971 human page views last month — 52% — were duplicates. It was the\n// largest single correction in either owner report, and the portal dashboard was\n// showing the uncorrected number to the customer. See\n// .docs/analysis/owner-reports/2026-07-justposh.md §7.\nlet autoRouteTrackingActive = false\n\n/**\n * Hand this App Insights instance to the cms conversion tracker, if one is capturing.\n *\n * THE CONTRACT, AND WHY IT IS A GLOBAL. `@duffcloudservices/cms` auto-installs a delegated\n * conversion listener (C-326) that starts capturing `tel:`/`sms:`/`mailto:`/booking/form\n * interactions before any telemetry SDK exists, buffering them until a transport shows up.\n * This package is a transport. Importing cms from here to say so would couple two packages\n * that are versioned and published independently — and would break the one property the\n * capture layer exists to have, which is that it never depends on a telemetry SDK.\n *\n * So the handshake is a single global function that cms publishes and anyone may call:\n *\n * ```js\n * window.__dcsConversionAttach?.((event) => appInsights.trackEvent(event))\n * ```\n *\n * A site with a bespoke telemetry boot (Bryan's is a hand-rolled copy of this composable)\n * adds exactly that line. Consumers of this package get it for free, here.\n *\n * Failing silently is correct: on a site without cms, or an older cms, the global is simply\n * absent and there is nothing to drain.\n */\nfunction attachConversionCapture(sink: (event: TelemetryEvent) => void): void {\n try {\n const w =\n typeof window !== 'undefined'\n ? (window as Window & {\n __dcsConversionAttach?: (s: (event: TelemetryEvent) => void) => number\n })\n : undefined\n w?.__dcsConversionAttach?.(sink)\n } catch (error) {\n console.warn('Failed to attach conversion capture to App Insights:', error)\n }\n}\n\n/**\n * Read the visitor/session ids off a live App Insights instance.\n *\n * The SDK's `context` shape is internal-ish and has moved between major\n * versions, so this reads it structurally and returns undefined on any\n * mismatch — {@link getJourneyContext} then falls back to the `ai_user` /\n * `ai_session` cookies. Never throws.\n */\nfunction readSdkIdentity(\n instance: ApplicationInsights | null,\n): { visitorId?: string; sessionId?: string } | undefined {\n if (!instance) return undefined\n try {\n const context = (\n instance as unknown as {\n context?: {\n user?: { id?: unknown }\n getSessionId?: () => unknown\n sessionManager?: { automaticSession?: { id?: unknown } }\n }\n }\n ).context\n if (!context) return undefined\n\n const visitorId =\n typeof context.user?.id === 'string' ? context.user.id : undefined\n\n let sessionId: string | undefined\n const fromGetter =\n typeof context.getSessionId === 'function' ? context.getSessionId() : undefined\n if (typeof fromGetter === 'string') {\n sessionId = fromGetter\n } else if (typeof context.sessionManager?.automaticSession?.id === 'string') {\n sessionId = context.sessionManager.automaticSession.id\n }\n\n if (!visitorId && !sessionId) return undefined\n return { visitorId, sessionId }\n } catch {\n return undefined\n }\n}\n\nfunction resolveAppVersion(explicit?: string): string {\n if (explicit) return explicit\n const w =\n typeof window !== 'undefined'\n ? (window as Window & { __APP_VERSION__?: string })\n : undefined\n return w?.__APP_VERSION__ ?? 'unknown'\n}\n\n/**\n * Azure Application Insights composable for DCS Vue apps.\n *\n * The connection string, cloud role, environment and app version are supplied\n * by the consumer (they read their own `import.meta.env` / build defines and\n * pass plain values in) so this package stays portable and build-tool agnostic.\n * The shared App Insights config — including the `disablePageUnloadEvents:\n * ['unload']` bfcache/Lighthouse fix — comes from {@link createTelemetryConfig}.\n */\nexport function useTelemetry(options: TelemetryOptions = {}) {\n const cloudRole = options.cloudRole ?? DEFAULT_CLOUD_ROLE\n const environment = options.environment ?? 'unknown'\n\n const isEnabled = computed(() => {\n return !!(options.connectionString || options.instrumentationKey)\n })\n\n function buildSharedProperties(): Record<string, string> {\n const appVersion = resolveAppVersion(options.appVersion)\n const referrerHost =\n typeof document !== 'undefined' && document.referrer\n ? (() => {\n try {\n return new URL(document.referrer).hostname\n } catch {\n return 'unknown'\n }\n })()\n : 'direct'\n\n return {\n app_version: appVersion,\n app_name: cloudRole,\n app_host: typeof window !== 'undefined' ? window.location.hostname : '',\n page_path: typeof window !== 'undefined' ? window.location.pathname : '',\n referrer_host: referrerHost,\n }\n }\n\n function buildEventProperties(\n additional?: Record<string, string>,\n ): Record<string, string> {\n return {\n timestamp: new Date().toISOString(),\n environment,\n ...buildSharedProperties(),\n ...additional,\n }\n }\n\n function enrichTelemetryEnvelope(envelope: TelemetryEnvelope) {\n if (!envelope.data) {\n envelope.data = {}\n }\n\n envelope.data.baseData = envelope.data.baseData || {}\n envelope.data.baseData.properties = envelope.data.baseData.properties || {}\n Object.assign(envelope.data.baseData.properties, buildSharedProperties())\n\n const appVersion = resolveAppVersion(options.appVersion)\n envelope.tags = envelope.tags || {}\n envelope.tags['ai.application.ver'] = appVersion\n envelope.tags['ai.cloud.role'] = cloudRole\n envelope.tags['ai.cloud.roleInstance'] =\n typeof window !== 'undefined' ? window.location.hostname : ''\n }\n\n const initialize = (): boolean => {\n try {\n // BEFORE the enabled check, and before anything can navigate. The SPA\n // router destroys document.referrer on the first route change, so the\n // landing referrer/utm has to be snapshotted here or it is gone. It is\n // deliberately not conditional on telemetry being configured: a site with\n // no connection string still benefits from first-touch attribution on its\n // stored form submissions.\n captureJourneyFirstTouch()\n\n if (isInitialized.value && appInsights) {\n return true\n }\n\n if (!isEnabled.value) {\n if (!warnedDisabled) {\n warnedDisabled = true\n // Loud, one-shot warning so this is obvious in DevTools. If you see\n // this in production, the build pipeline is not injecting the App\n // Insights connection string — every track* call below silently\n // no-ops. See web/ site-audit F2.\n console.warn(\n '[telemetry] Application Insights DISABLED: no connection string ' +\n 'or instrumentation key was provided at build time. ' +\n 'All telemetry calls will silently no-op.',\n )\n }\n return false\n }\n\n const resolvedConfig = createTelemetryConfig({\n connectionString: options.connectionString,\n instrumentationKey: options.instrumentationKey,\n enableAutoRouteTracking: options.enableAutoRouteTracking,\n dev: options.dev,\n disablePageUnloadEvents: options.disablePageUnloadEvents,\n overrides: options.overrides,\n })\n // Remember whether the SDK is counting page views for us, so trackPageView()\n // can refuse to count them a second time.\n autoRouteTrackingActive = resolvedConfig.enableAutoRouteTracking !== false\n\n appInsights = new ApplicationInsights({ config: resolvedConfig })\n\n appInsights.loadAppInsights()\n appInsights.addTelemetryInitializer((envelope) => {\n try {\n enrichTelemetryEnvelope(envelope as TelemetryEnvelope)\n } catch (error) {\n console.warn('Failed to add web telemetry context:', error)\n }\n\n return true\n })\n\n // Prefer the live SDK context over cookie parsing for the journey join\n // key: the instance is authoritative, and it stays correct even if the\n // cookies are blocked or renamed by a future SDK version.\n setJourneyIdentityResolver(() => readSdkIdentity(appInsights))\n\n isInitialized.value = true\n initializationError.value = null\n console.log('Application Insights initialized successfully')\n\n // Drain any conversion clicks captured before this transport existed, and stay\n // attached for the rest of the session. See attachConversionCapture().\n attachConversionCapture(trackEvent)\n\n return true\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown initialization error'\n initializationError.value = errorMessage\n console.error('Failed to initialize Application Insights:', error)\n return false\n }\n }\n\n const trackEvent = (event: TelemetryEvent) => {\n if (!appInsights || !isInitialized.value) {\n if (!warnedNotInitialized) {\n warnedNotInitialized = true\n console.warn(\n `[telemetry] Application Insights not initialized — dropping event \"${event.name}\" ` +\n '(and silencing further \"not initialized\" warnings for this session).',\n )\n }\n return\n }\n\n try {\n appInsights.trackEvent({\n name: event.name,\n properties: buildEventProperties(event.properties),\n measurements: event.measurements,\n })\n } catch (error) {\n console.error('Failed to track event:', error)\n }\n }\n\n /**\n * Send a page view.\n *\n * REFUSES TO SEND when the SDK's own `enableAutoRouteTracking` is active, because\n * the SDK has already emitted a pageView for this navigation and sending another\n * double-counts the page. Pass `{ force: true }` only when you have verified that\n * auto-route tracking genuinely does not fire for your routing setup — and expect\n * to justify it, because a page-view count that is silently 2x is the defect this\n * guard exists to prevent (C-288: 52% of one site's page views were duplicates).\n *\n * If you want manual page views, turn the SDK's tracking off:\n * `useTelemetry({ enableAutoRouteTracking: false })`. One tracker, one count.\n */\n const trackPageView = (pageView?: TelemetryPageView) => {\n if (!appInsights || !isInitialized.value) {\n console.warn('Application Insights not initialized, skipping page view tracking')\n return\n }\n\n if (autoRouteTrackingActive && !pageView?.force) {\n if (!warnedDoubleCount) {\n warnedDoubleCount = true\n console.warn(\n '[telemetry] trackPageView() SKIPPED to prevent double-counting: this ' +\n 'instance was initialized with enableAutoRouteTracking, so the App ' +\n 'Insights SDK already reports one page view per navigation. Pass ' +\n '{ enableAutoRouteTracking: false } to useTelemetry() if you want to ' +\n 'report page views manually instead.',\n )\n }\n return\n }\n\n try {\n // Attach the full document.referrer (not just hostname) so we can\n // attribute traffic sources in App Insights pageViews.\n const referrer =\n typeof document !== 'undefined' ? (document.referrer ?? '') : ''\n appInsights.trackPageView({\n name:\n pageView?.name ||\n (typeof document !== 'undefined' ? document.title : undefined),\n uri:\n pageView?.uri ||\n (typeof window !== 'undefined' ? window.location.href : undefined),\n properties: buildEventProperties({\n referrer,\n ...pageView?.properties,\n }),\n measurements: pageView?.measurements,\n })\n } catch (error) {\n console.error('Failed to track page view:', error)\n }\n }\n\n /**\n * Convenience helper for primary CTA conversion measurement. Fires a\n * `cta_click` custom event so conversion rate per landing page can be\n * computed in App Insights / KQL.\n */\n const trackCtaClick = (label: string, page?: string) => {\n trackEvent({\n name: 'cta_click',\n properties: {\n label,\n page:\n page ??\n (typeof window !== 'undefined' ? window.location.pathname : ''),\n },\n })\n }\n\n const trackException = (exception: TelemetryException) => {\n if (!appInsights || !isInitialized.value) {\n console.warn('Application Insights not initialized, skipping exception tracking')\n return\n }\n\n try {\n appInsights.trackException({\n exception: exception.exception,\n properties: buildEventProperties({\n url: typeof window !== 'undefined' ? window.location.href : '',\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : '',\n ...exception.properties,\n }),\n measurements: exception.measurements,\n })\n } catch (error) {\n console.error('Failed to track exception:', error)\n }\n }\n\n const trackDependency = (dependency: TelemetryDependency) => {\n if (!appInsights || !isInitialized.value) {\n console.warn('Application Insights not initialized, skipping dependency tracking')\n return\n }\n\n try {\n appInsights.trackDependencyData({\n id: `dep-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,\n name: dependency.name,\n data: dependency.data,\n duration: dependency.duration,\n success: dependency.success,\n responseCode: dependency.resultCode || 0,\n properties: buildEventProperties(dependency.properties),\n measurements: dependency.measurements,\n })\n } catch (error) {\n console.error('Failed to track dependency:', error)\n }\n }\n\n const trackMetric = (metric: TelemetryMetric) => {\n if (!appInsights || !isInitialized.value) {\n console.warn('Application Insights not initialized, skipping metric tracking')\n return\n }\n\n try {\n appInsights.trackMetric({\n name: metric.name,\n average: metric.average,\n sampleCount: metric.sampleCount,\n min: metric.min,\n max: metric.max,\n properties: buildEventProperties(metric.properties),\n })\n } catch (error) {\n console.error('Failed to track metric:', error)\n }\n }\n\n const flush = () => {\n if (!appInsights || !isInitialized.value) return\n\n try {\n appInsights.flush()\n } catch (error) {\n console.error('Failed to flush telemetry:', error)\n }\n }\n\n /**\n * The same guard `trackPageView` applies, on the other door into the page-view\n * pipeline. `stopTrackPage` emits a page view just as surely as `trackPageView`\n * does, so leaving it unguarded left the C-288 double-count fully reachable —\n * and `web/` walked straight through it: the marketing site called the manual\n * pair from its router while `enableAutoRouteTracking` kept its `true` default,\n * and live KQL (2026-07-31) caught one /contact navigation writing THREE\n * AppPageViews rows in 700ms. A guard on one entry point is not a guard.\n *\n * Both halves of the pair check this so they stay balanced: skipping only the\n * stop would leave an unterminated page-timing entry inside the SDK.\n */\n const manualPageTrackingIsRedundant = (): boolean => {\n if (!autoRouteTrackingActive) return false\n if (!warnedDoubleCount) {\n warnedDoubleCount = true\n console.warn(\n '[telemetry] startTrackPage()/stopTrackPage() SKIPPED to prevent ' +\n 'double-counting: this instance was initialized with ' +\n 'enableAutoRouteTracking, so the App Insights SDK already reports a ' +\n 'page view per navigation. Pass { enableAutoRouteTracking: false } to ' +\n 'useTelemetry() if you want to time and report page views manually.',\n )\n }\n return true\n }\n\n const startTrackPage = (name?: string) => {\n if (!appInsights || !isInitialized.value) return\n if (manualPageTrackingIsRedundant()) return\n\n try {\n appInsights.startTrackPage(name)\n } catch (error) {\n console.error('Failed to start track page:', error)\n }\n }\n\n const stopTrackPage = (\n name?: string,\n url?: string,\n properties?: Record<string, string>,\n measurements?: Record<string, number>,\n ) => {\n if (!appInsights || !isInitialized.value) return\n if (manualPageTrackingIsRedundant()) return\n\n try {\n appInsights.stopTrackPage(name, url, buildEventProperties(properties), measurements)\n } catch (error) {\n console.error('Failed to stop track page:', error)\n }\n }\n\n return {\n isEnabled,\n isInitialized,\n initializationError,\n initialize,\n flush,\n startTrackPage,\n stopTrackPage,\n trackDependency,\n trackEvent,\n trackException,\n trackMetric,\n trackPageView,\n trackCtaClick,\n }\n}\n\n/** The full public API returned by {@link useTelemetry}. */\nexport type TelemetryApi = ReturnType<typeof useTelemetry>\n"]}
1
+ {"version":3,"sources":["../src/syntheticCapture.ts","../src/composable.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Synthetic-capture detection (C-614).\n *\n * The post-deploy \"Capture page snapshots\" CI step drives real Chromium against\n * the LIVE production URL of the site it just deployed, so the site's telemetry\n * boots exactly as it would for a visitor and every page the capture walked is\n * recorded as a production pageView — during our own deploy run. Measured on\n * coron8 over the 8 days to 2026-08-11: 49 synthetic pageViews against 14\n * organic, 78% of the entire store, all 49 on 08-11 across 5 deploys versus 3\n * real visits that day.\n *\n * The capture tooling marks every URL it loads (`packages/cli/src/commands/\n * capture-snapshots.ts` and each site's own `scripts/capture-snapshots.*`).\n * `initialize()` reads that mark and declines, so nothing is emitted at all.\n * Suppressing here rather than tagging and filtering downstream keeps the\n * denominator honest in every read path at once instead of obliging each of\n * them to remember an exclusion — the C-430 preview/localhost blend is the\n * recorded UNSOLVED residual that shape produces.\n *\n * DELIBERATE DUPLICATION. `@duffcloudservices/cms` carries the same predicate in\n * its own `syntheticCapture.ts`. These two packages are intentionally decoupled\n * — this one is the low-level telemetry package (peer deps: the App Insights SDK\n * and vue, nothing else), cms is the site-content package — and a dependency\n * edge between them to share ten lines of pure string matching would cost more\n * than the duplication does. The parameter list is the contract; keep the two\n * lists identical, and note that the CI drift guard in the canonical\n * site-deploy workflow greps for these same names.\n */\n\n/** Query parameters that mark a document as an automated capture, not a visit. */\nexport const SYNTHETIC_CAPTURE_PARAMS = ['dcs-hide-ribbon', 'dcs-no-telemetry'] as const\n\n/**\n * Whether this document is an automated capture rather than a visit worth counting.\n *\n * Reads `window.location.search` on every call rather than caching, so a\n * client-side route change is judged on the CURRENT URL. Returns `false` on the\n * server, and `false` when the query string cannot be read: over-suppression\n * deletes real visits and leaves no trace to notice it by, so this fails OPEN.\n */\nexport function syntheticCaptureDetected(): boolean {\n if (typeof window === 'undefined') return false\n\n try {\n const params = new URLSearchParams(window.location.search)\n return SYNTHETIC_CAPTURE_PARAMS.some((param) => params.has(param))\n } catch {\n return false\n }\n}\n","// TYPE-ONLY on purpose — this is the whole point of C-601's lazy path.\n//\n// A *value* import of the App Insights web SDK here folds the SDK into whatever\n// chunk imports this module, which for a site is the ENTRY chunk. Measured on the\n// bryans-handyman-solutions pilot (2026-08-11): adopting this package as published\n// moved 192,265B raw / 77,920B gzip of SDK off its own async chunk and onto the\n// entry chunk (+90.4% raw / +101.2% gzip), while the package's own code was only\n// +4.8KB of that. Total bytes barely moved — the SDK simply relocated onto the\n// critical path, defeating the sites' deliberate requestIdleCallback deferral.\n//\n// `import type` is erased by tsc/tsup, so it emits no runtime import at all. The\n// SDK is reached exactly once, through the dynamic `import()` in `loadSdk()`\n// below, which every bundler emits as a separate async chunk. Pinned by\n// `lazySdk.test.ts` — reintroducing a value import here turns that spec red.\nimport type { ApplicationInsights } from '@microsoft/applicationinsights-web'\nimport { computed, ref } from 'vue'\n\nimport { DEFAULT_CLOUD_ROLE, createTelemetryConfig } from './config'\nimport { captureJourneyFirstTouch, setJourneyIdentityResolver } from './journey'\nimport { syntheticCaptureDetected } from './syntheticCapture'\nimport type {\n TelemetryDependency,\n TelemetryEvent,\n TelemetryException,\n TelemetryMetric,\n TelemetryOptions,\n TelemetryPageView,\n} from './types'\n\ninterface TelemetryEnvelope {\n data?: {\n baseData?: {\n properties?: Record<string, string>\n }\n }\n tags?: Record<string, string>\n}\n\n// Module-level singleton state: one App Insights instance per app bundle,\n// shared across every `useTelemetry()` call (matches the original per-app\n// composable). The reactive refs let views observe init state.\nlet appInsights: ApplicationInsights | null = null\nconst isInitialized = ref(false)\nconst initializationError = ref<string | null>(null)\n\n/**\n * The in-flight lazy SDK load, or `null` when none is running.\n *\n * This is the single-init guard AND the \"is a load in flight\" signal the pre-init\n * buffer keys off. Two concurrent `initialize()` callers — a `main.ts` boot and a\n * component's `onMounted`, say — must produce ONE dynamic import and ONE\n * `new ApplicationInsights(...)`, or the site gets two SDK instances racing the\n * same connection string. Mirrors the guarded initializer the sites already run\n * (kduff-homes `useTelemetry.ts` `initializationPromise`).\n *\n * Non-null also means \"buffer, don't drop\": see {@link bufferWhileLoading}.\n */\nlet initPromise: Promise<boolean> | null = null\n\n/** One already-built SDK call, replayed verbatim once the SDK arrives. */\ntype PendingSdkCall = (instance: ApplicationInsights) => void\n\n/**\n * Calls made after `initialize()` but before the lazy SDK chunk resolves.\n *\n * Without this, laziness would silently cost data: a site that calls\n * `initialize()` and immediately reports a landing pageView or a `cta_click`\n * would have both dropped by the `!isInitialized` guards, because the SDK is\n * still in flight over the network. The site-local composables this package\n * replaces DO drop those calls (bryans/kduff both early-return) — buffering is a\n * strict improvement on the pattern being adopted, not a port of it.\n *\n * Payloads are built at CALL time, so `timestamp`, `page_path` and `referrer_host`\n * describe the moment the event happened, not the moment the chunk landed. Order\n * is preserved.\n */\nconst pendingSdkCalls: PendingSdkCall[] = []\n\n/**\n * Bound on the buffer. A load that never settles (offline, blocked CDN, an\n * ad-blocker eating the SDK chunk) must not grow an unbounded array for the rest\n * of the session on a long-lived SPA. 100 is far above the handful of events a\n * real pre-init window sees and far below anything that matters for memory.\n */\nconst PENDING_CALL_LIMIT = 100\nlet warnedBufferFull = false\n\n/**\n * Take ownership of a track* call while the SDK chunk is in flight.\n *\n * Returns `true` when this function has handled the call (buffered it, or\n * deliberately dropped it because the buffer is full) and the caller must return.\n * Returns `false` when no load is in flight — the caller then keeps its existing\n * \"not initialized\" warn-and-drop behaviour, unchanged. That distinction is what\n * stops an unconfigured site (never calls a successful `initialize()`) from\n * accumulating a buffer nobody will ever drain.\n */\nfunction bufferWhileLoading(call: PendingSdkCall): boolean {\n if (!initPromise) return false\n\n if (pendingSdkCalls.length >= PENDING_CALL_LIMIT) {\n if (!warnedBufferFull) {\n warnedBufferFull = true\n console.warn(\n `[telemetry] pre-init buffer full (${PENDING_CALL_LIMIT} calls) — dropping ` +\n 'telemetry until the Application Insights SDK chunk finishes loading. ' +\n 'If you see this, the SDK chunk is failing to load, not merely slow.',\n )\n }\n return true\n }\n\n pendingSdkCalls.push(call)\n return true\n}\n\n/** Replay everything buffered during the load, in order, then clear. */\nfunction drainPendingSdkCalls(instance: ApplicationInsights): void {\n const calls = pendingSdkCalls.splice(0, pendingSdkCalls.length)\n for (const call of calls) {\n try {\n call(instance)\n } catch (error) {\n console.error('Failed to replay buffered telemetry:', error)\n }\n }\n}\n\n/** Abandon anything buffered — the SDK is never arriving. */\nfunction discardPendingSdkCalls(): void {\n pendingSdkCalls.length = 0\n}\n\n/**\n * Load the App Insights web SDK constructor on demand.\n *\n * The ONE runtime reference to `@microsoft/applicationinsights-web` in this\n * package. Kept as a bare dynamic `import()` (not wrapped in a variable specifier)\n * so bundlers can statically see the dependency and emit it as its own async\n * chunk — which is exactly the shape the sites hand-rolled and the shape this\n * package existed to fold up.\n */\nasync function loadSdk(): Promise<typeof ApplicationInsights> {\n const module = await import('@microsoft/applicationinsights-web')\n return module.ApplicationInsights\n}\n\n/** Monotonic-ish clock for the pre-init page-timing pair. */\nfunction nowMs(): number {\n return typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now()\n : Date.now()\n}\n\n/**\n * Pre-init `startTrackPage` timers, keyed by resolved page name.\n *\n * The SDK's own start/stop pair CANNOT be replayed faithfully after the fact: its\n * internal timer would start at REPLAY time, so a page whose load straddled the\n * lazy import would report a ~0ms duration. So a pair that straddles the load is\n * timed here and replayed as the equivalent `trackPageView`.\n *\n * That is not an approximation — it is precisely what the SDK does internally.\n * `AnalyticsPlugin.stopTrackPage` delegates to `_pageTracking.action`, which sets\n * `properties.duration = duration.toString()` and calls `sendPageViewInternal`;\n * `sendPageViewInternal` then derives `startTime` from `properties.duration` when\n * no `startTime` is present (verified in applicationinsights-analytics-js 3.3.10,\n * `dist-es5/JavaScriptSDK/AnalyticsPlugin.js`). Same wire shape, real duration —\n * and, like the SDK, `duration` is left on the custom properties.\n */\nconst preInitPageTimings = new Map<string, number>()\n\n/** The name the SDK itself would use: the argument, else `document.title`. */\nfunction resolvePageName(name?: string): string {\n if (typeof name === 'string') return name\n return typeof document !== 'undefined' ? document.title : ''\n}\n\n// One-shot guards so we surface \"App Insights is not configured\" loudly in\n// DevTools the FIRST time the app tries to use it, but don't spam the console\n// for every subsequent trackEvent / trackPageView call. See web/ site-audit F2\n// — `customEvents = 0` for 7d was a missing connection string silently\n// no-op'ing every telemetry call; the loud warning makes that mode visible.\nlet warnedDisabled = false\nlet warnedNotInitialized = false\nlet warnedDoubleCount = false\n\n// Whether the live instance was created with the SDK's own SPA route tracking on.\n// When it is, the SDK already emits one pageView per navigation, so a consumer that\n// ALSO calls trackPageView() on route change counts every page twice.\n//\n// This is not hypothetical (C-288, 2026-07-25): the Just Posh site ran both at once\n// and 509 of its 971 human page views last month — 52% — were duplicates. It was the\n// largest single correction in either owner report, and the portal dashboard was\n// showing the uncorrected number to the customer. See\n// .docs/analysis/owner-reports/2026-07-justposh.md §7.\nlet autoRouteTrackingActive = false\n\n/**\n * Hand this App Insights instance to the cms conversion tracker, if one is capturing.\n *\n * THE CONTRACT, AND WHY IT IS A GLOBAL. `@duffcloudservices/cms` auto-installs a delegated\n * conversion listener (C-326) that starts capturing `tel:`/`sms:`/`mailto:`/booking/form\n * interactions before any telemetry SDK exists, buffering them until a transport shows up.\n * This package is a transport. Importing cms from here to say so would couple two packages\n * that are versioned and published independently — and would break the one property the\n * capture layer exists to have, which is that it never depends on a telemetry SDK.\n *\n * So the handshake is a single global function that cms publishes and anyone may call:\n *\n * ```js\n * window.__dcsConversionAttach?.((event) => appInsights.trackEvent(event))\n * ```\n *\n * A site with a bespoke telemetry boot (Bryan's is a hand-rolled copy of this composable)\n * adds exactly that line. Consumers of this package get it for free, here.\n *\n * Failing silently is correct: on a site without cms, or an older cms, the global is simply\n * absent and there is nothing to drain.\n */\nfunction attachConversionCapture(sink: (event: TelemetryEvent) => void): void {\n try {\n const w =\n typeof window !== 'undefined'\n ? (window as Window & {\n __dcsConversionAttach?: (s: (event: TelemetryEvent) => void) => number\n })\n : undefined\n w?.__dcsConversionAttach?.(sink)\n } catch (error) {\n console.warn('Failed to attach conversion capture to App Insights:', error)\n }\n}\n\n/**\n * Read the visitor/session ids off a live App Insights instance.\n *\n * The SDK's `context` shape is internal-ish and has moved between major\n * versions, so this reads it structurally and returns undefined on any\n * mismatch — {@link getJourneyContext} then falls back to the `ai_user` /\n * `ai_session` cookies. Never throws.\n */\nfunction readSdkIdentity(\n instance: ApplicationInsights | null,\n): { visitorId?: string; sessionId?: string } | undefined {\n if (!instance) return undefined\n try {\n const context = (\n instance as unknown as {\n context?: {\n user?: { id?: unknown }\n getSessionId?: () => unknown\n sessionManager?: { automaticSession?: { id?: unknown } }\n }\n }\n ).context\n if (!context) return undefined\n\n const visitorId =\n typeof context.user?.id === 'string' ? context.user.id : undefined\n\n let sessionId: string | undefined\n const fromGetter =\n typeof context.getSessionId === 'function' ? context.getSessionId() : undefined\n if (typeof fromGetter === 'string') {\n sessionId = fromGetter\n } else if (typeof context.sessionManager?.automaticSession?.id === 'string') {\n sessionId = context.sessionManager.automaticSession.id\n }\n\n if (!visitorId && !sessionId) return undefined\n return { visitorId, sessionId }\n } catch {\n return undefined\n }\n}\n\nfunction resolveAppVersion(explicit?: string): string {\n if (explicit) return explicit\n const w =\n typeof window !== 'undefined'\n ? (window as Window & { __APP_VERSION__?: string })\n : undefined\n return w?.__APP_VERSION__ ?? 'unknown'\n}\n\n/**\n * Azure Application Insights composable for DCS Vue apps.\n *\n * The connection string, cloud role, environment and app version are supplied\n * by the consumer (they read their own `import.meta.env` / build defines and\n * pass plain values in) so this package stays portable and build-tool agnostic.\n * The shared App Insights config — including the `disablePageUnloadEvents:\n * ['unload']` bfcache/Lighthouse fix — comes from {@link createTelemetryConfig}.\n *\n * ## The SDK loads LAZILY (C-601)\n *\n * `initialize()` still returns `boolean` synchronously — \"telemetry is enabled and\n * the SDK load has started\" — but the App Insights web SDK itself arrives through\n * a dynamic `import()`, so a bundler emits it as its own async chunk instead of\n * folding ~192KB raw / ~78KB gzip into the consumer's entry chunk. Adopting this\n * package therefore no longer moves the SDK onto the critical path.\n *\n * Three things make that safe:\n *\n * 1. **Buffered, not dropped.** track* calls made between `initialize()` and the\n * chunk landing are queued with their call-time properties and replayed in\n * order (bounded at {@link PENDING_CALL_LIMIT}). The site-local composables this\n * package replaces dropped them.\n * 2. **One init.** Concurrent `initialize()` callers share one `import()` and one\n * `new ApplicationInsights(...)` via {@link initPromise}.\n * 3. **Guards stay synchronous.** `enableAutoRouteTracking` is resolved before the\n * `await`, so the C-288 double-count guard is armed for calls made during the\n * load, not only after it.\n *\n * `whenReady()` is there for the rare caller that must sequence work after the SDK\n * exists. You should not need it to avoid losing events.\n */\nexport function useTelemetry(options: TelemetryOptions = {}) {\n const cloudRole = options.cloudRole ?? DEFAULT_CLOUD_ROLE\n const environment = options.environment ?? 'unknown'\n\n const isEnabled = computed(() => {\n return !!(options.connectionString || options.instrumentationKey)\n })\n\n function buildSharedProperties(): Record<string, string> {\n const appVersion = resolveAppVersion(options.appVersion)\n const referrerHost =\n typeof document !== 'undefined' && document.referrer\n ? (() => {\n try {\n return new URL(document.referrer).hostname\n } catch {\n return 'unknown'\n }\n })()\n : 'direct'\n\n return {\n app_version: appVersion,\n app_name: cloudRole,\n app_host: typeof window !== 'undefined' ? window.location.hostname : '',\n page_path: typeof window !== 'undefined' ? window.location.pathname : '',\n referrer_host: referrerHost,\n }\n }\n\n function buildEventProperties(\n additional?: Record<string, string>,\n ): Record<string, string> {\n return {\n timestamp: new Date().toISOString(),\n environment,\n ...buildSharedProperties(),\n ...additional,\n }\n }\n\n function enrichTelemetryEnvelope(envelope: TelemetryEnvelope) {\n if (!envelope.data) {\n envelope.data = {}\n }\n\n envelope.data.baseData = envelope.data.baseData || {}\n envelope.data.baseData.properties = envelope.data.baseData.properties || {}\n Object.assign(envelope.data.baseData.properties, buildSharedProperties())\n\n const appVersion = resolveAppVersion(options.appVersion)\n envelope.tags = envelope.tags || {}\n envelope.tags['ai.application.ver'] = appVersion\n envelope.tags['ai.cloud.role'] = cloudRole\n envelope.tags['ai.cloud.roleInstance'] =\n typeof window !== 'undefined' ? window.location.hostname : ''\n }\n\n const initialize = (): boolean => {\n try {\n // BEFORE the enabled check, and before anything can navigate. The SPA\n // router destroys document.referrer on the first route change, so the\n // landing referrer/utm has to be snapshotted here or it is gone. It is\n // deliberately not conditional on telemetry being configured: a site with\n // no connection string still benefits from first-touch attribution on its\n // stored form submissions.\n captureJourneyFirstTouch()\n\n // C-614: the post-deploy snapshot-capture step walks the LIVE site with\n // real Chromium. Declining BEFORE the SDK is constructed means a capture\n // pass emits no pageView, event or exception at all — every track* call\n // below already no-ops while `isInitialized` is false. Checked after the\n // journey snapshot above so that deliberately unconditional behaviour is\n // left exactly as it was.\n if (syntheticCaptureDetected()) {\n return false\n }\n\n if (isInitialized.value && appInsights) {\n return true\n }\n\n // A load is already in flight (or already finished) — do NOT start a second\n // one. Returning true keeps the caller's contract (\"telemetry is on\"); the\n // awaitable answer is `whenReady()`.\n if (initPromise) {\n return true\n }\n\n if (!isEnabled.value) {\n if (!warnedDisabled) {\n warnedDisabled = true\n // Loud, one-shot warning so this is obvious in DevTools. If you see\n // this in production, the build pipeline is not injecting the App\n // Insights connection string — every track* call below silently\n // no-ops. See web/ site-audit F2.\n console.warn(\n '[telemetry] Application Insights DISABLED: no connection string ' +\n 'or instrumentation key was provided at build time. ' +\n 'All telemetry calls will silently no-op.',\n )\n }\n return false\n }\n\n const resolvedConfig = createTelemetryConfig({\n connectionString: options.connectionString,\n instrumentationKey: options.instrumentationKey,\n enableAutoRouteTracking: options.enableAutoRouteTracking,\n dev: options.dev,\n disablePageUnloadEvents: options.disablePageUnloadEvents,\n overrides: options.overrides,\n })\n // Remember whether the SDK is counting page views for us, so trackPageView()\n // can refuse to count them a second time.\n //\n // Resolved SYNCHRONOUSLY, before the lazy import, on purpose: the C-288\n // double-count guard must be armed for the calls that arrive DURING the load,\n // or a site with auto-route tracking on would buffer manual page views and\n // then replay them into the duplicate the guard exists to prevent.\n autoRouteTrackingActive = resolvedConfig.enableAutoRouteTracking !== false\n\n initPromise = (async (): Promise<boolean> => {\n try {\n const ApplicationInsightsCtor = await loadSdk()\n\n const instance = new ApplicationInsightsCtor({ config: resolvedConfig })\n instance.loadAppInsights()\n instance.addTelemetryInitializer((envelope) => {\n try {\n enrichTelemetryEnvelope(envelope as TelemetryEnvelope)\n } catch (error) {\n console.warn('Failed to add web telemetry context:', error)\n }\n\n return true\n })\n\n // Published LAST, after the SDK is wired: `appInsights` is what every\n // track* guard reads, so assigning it earlier would let a call slip\n // through to a half-configured instance (no telemetry initializer, so no\n // app_version / cloud role on the envelope).\n appInsights = instance\n\n // Prefer the live SDK context over cookie parsing for the journey join\n // key: the instance is authoritative, and it stays correct even if the\n // cookies are blocked or renamed by a future SDK version.\n setJourneyIdentityResolver(() => readSdkIdentity(appInsights))\n\n isInitialized.value = true\n initializationError.value = null\n console.log('Application Insights initialized successfully')\n\n // Anything that fired between initialize() and this moment, in order.\n drainPendingSdkCalls(instance)\n\n // Drain any conversion clicks captured before this transport existed, and\n // stay attached for the rest of the session. See attachConversionCapture().\n attachConversionCapture(trackEvent)\n\n return true\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown initialization error'\n initializationError.value = errorMessage\n console.error('Failed to initialize Application Insights:', error)\n // The SDK is not coming. Release the buffer rather than holding events\n // for a drain that will never happen, and clear the in-flight marker so\n // subsequent track* calls warn-and-drop instead of buffering forever.\n discardPendingSdkCalls()\n preInitPageTimings.clear()\n initPromise = null\n return false\n }\n })()\n\n return true\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown initialization error'\n initializationError.value = errorMessage\n console.error('Failed to initialize Application Insights:', error)\n return false\n }\n }\n\n /**\n * Resolves when the lazy App Insights chunk has finished loading (or failed).\n *\n * ADDITIVE — `initialize()` keeps its synchronous `boolean` return, so every\n * existing caller (`web/src/main.ts`, the site adapters) is untouched. Use this\n * when you genuinely need to sequence work after the SDK exists; you should not\n * need it merely to avoid losing events, because calls made during the load are\n * buffered and replayed.\n *\n * `false` means \"no live SDK\": telemetry is disabled, a synthetic capture pass\n * declined it, `initialize()` was never called, or the chunk failed to load.\n */\n const whenReady = (): Promise<boolean> => {\n if (isInitialized.value && appInsights) return Promise.resolve(true)\n return initPromise ?? Promise.resolve(false)\n }\n\n const trackEvent = (event: TelemetryEvent) => {\n if (!appInsights || !isInitialized.value) {\n // Built here, not at drain time, so the timestamp/page_path describe when the\n // event happened rather than when the SDK chunk landed.\n const payload = {\n name: event.name,\n properties: buildEventProperties(event.properties),\n measurements: event.measurements,\n }\n if (bufferWhileLoading((instance) => instance.trackEvent(payload))) return\n\n if (!warnedNotInitialized) {\n warnedNotInitialized = true\n console.warn(\n `[telemetry] Application Insights not initialized — dropping event \"${event.name}\" ` +\n '(and silencing further \"not initialized\" warnings for this session).',\n )\n }\n return\n }\n\n try {\n appInsights.trackEvent({\n name: event.name,\n properties: buildEventProperties(event.properties),\n measurements: event.measurements,\n })\n } catch (error) {\n console.error('Failed to track event:', error)\n }\n }\n\n /**\n * Send a page view.\n *\n * REFUSES TO SEND when the SDK's own `enableAutoRouteTracking` is active, because\n * the SDK has already emitted a pageView for this navigation and sending another\n * double-counts the page. Pass `{ force: true }` only when you have verified that\n * auto-route tracking genuinely does not fire for your routing setup — and expect\n * to justify it, because a page-view count that is silently 2x is the defect this\n * guard exists to prevent (C-288: 52% of one site's page views were duplicates).\n *\n * If you want manual page views, turn the SDK's tracking off:\n * `useTelemetry({ enableAutoRouteTracking: false })`. One tracker, one count.\n */\n const trackPageView = (pageView?: TelemetryPageView) => {\n // The double-count guard runs FIRST, before the init/buffer split. It is a\n // question about CONFIG (is the SDK already counting routes?), not about load\n // state, and `autoRouteTrackingActive` is resolved synchronously by\n // initialize(). Checking it after the buffer would let manual page views queue\n // during the load and then replay into the duplicate this guard exists to\n // prevent (C-288: 52% of one site's page views).\n if (autoRouteTrackingActive && !pageView?.force) {\n if (!warnedDoubleCount) {\n warnedDoubleCount = true\n console.warn(\n '[telemetry] trackPageView() SKIPPED to prevent double-counting: this ' +\n 'instance was initialized with enableAutoRouteTracking, so the App ' +\n 'Insights SDK already reports one page view per navigation. Pass ' +\n '{ enableAutoRouteTracking: false } to useTelemetry() if you want to ' +\n 'report page views manually instead.',\n )\n }\n return\n }\n\n // Attach the full document.referrer (not just hostname) so we can\n // attribute traffic sources in App Insights pageViews.\n const referrer = typeof document !== 'undefined' ? (document.referrer ?? '') : ''\n const payload = {\n name:\n pageView?.name ||\n (typeof document !== 'undefined' ? document.title : undefined),\n uri:\n pageView?.uri ||\n (typeof window !== 'undefined' ? window.location.href : undefined),\n properties: buildEventProperties({\n referrer,\n ...pageView?.properties,\n }),\n measurements: pageView?.measurements,\n }\n\n if (!appInsights || !isInitialized.value) {\n if (bufferWhileLoading((instance) => instance.trackPageView(payload))) return\n console.warn('Application Insights not initialized, skipping page view tracking')\n return\n }\n\n try {\n appInsights.trackPageView(payload)\n } catch (error) {\n console.error('Failed to track page view:', error)\n }\n }\n\n /**\n * Convenience helper for primary CTA conversion measurement. Fires a\n * `cta_click` custom event so conversion rate per landing page can be\n * computed in App Insights / KQL.\n */\n const trackCtaClick = (label: string, page?: string) => {\n trackEvent({\n name: 'cta_click',\n properties: {\n label,\n page:\n page ??\n (typeof window !== 'undefined' ? window.location.pathname : ''),\n },\n })\n }\n\n const trackException = (exception: TelemetryException) => {\n const payload = {\n exception: exception.exception,\n properties: buildEventProperties({\n url: typeof window !== 'undefined' ? window.location.href : '',\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : '',\n ...exception.properties,\n }),\n measurements: exception.measurements,\n }\n\n if (!appInsights || !isInitialized.value) {\n // Exceptions are the calls that most need the buffer: a boot-time error is\n // exactly the class of failure that fires while the SDK chunk is still in\n // flight, and it is the class you least want silently dropped.\n if (bufferWhileLoading((instance) => instance.trackException(payload))) return\n console.warn('Application Insights not initialized, skipping exception tracking')\n return\n }\n\n try {\n appInsights.trackException(payload)\n } catch (error) {\n console.error('Failed to track exception:', error)\n }\n }\n\n const trackDependency = (dependency: TelemetryDependency) => {\n const payload = {\n id: `dep-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,\n name: dependency.name,\n data: dependency.data,\n duration: dependency.duration,\n success: dependency.success,\n responseCode: dependency.resultCode || 0,\n properties: buildEventProperties(dependency.properties),\n measurements: dependency.measurements,\n }\n\n if (!appInsights || !isInitialized.value) {\n if (bufferWhileLoading((instance) => instance.trackDependencyData(payload))) return\n console.warn('Application Insights not initialized, skipping dependency tracking')\n return\n }\n\n try {\n appInsights.trackDependencyData(payload)\n } catch (error) {\n console.error('Failed to track dependency:', error)\n }\n }\n\n const trackMetric = (metric: TelemetryMetric) => {\n const payload = {\n name: metric.name,\n average: metric.average,\n sampleCount: metric.sampleCount,\n min: metric.min,\n max: metric.max,\n properties: buildEventProperties(metric.properties),\n }\n\n if (!appInsights || !isInitialized.value) {\n if (bufferWhileLoading((instance) => instance.trackMetric(payload))) return\n console.warn('Application Insights not initialized, skipping metric tracking')\n return\n }\n\n try {\n appInsights.trackMetric(payload)\n } catch (error) {\n console.error('Failed to track metric:', error)\n }\n }\n\n const flush = () => {\n if (!appInsights || !isInitialized.value) return\n\n try {\n appInsights.flush()\n } catch (error) {\n console.error('Failed to flush telemetry:', error)\n }\n }\n\n /**\n * The same guard `trackPageView` applies, on the other door into the page-view\n * pipeline. `stopTrackPage` emits a page view just as surely as `trackPageView`\n * does, so leaving it unguarded left the C-288 double-count fully reachable —\n * and `web/` walked straight through it: the marketing site called the manual\n * pair from its router while `enableAutoRouteTracking` kept its `true` default,\n * and live KQL (2026-07-31) caught one /contact navigation writing THREE\n * AppPageViews rows in 700ms. A guard on one entry point is not a guard.\n *\n * Both halves of the pair check this so they stay balanced: skipping only the\n * stop would leave an unterminated page-timing entry inside the SDK.\n */\n const manualPageTrackingIsRedundant = (): boolean => {\n if (!autoRouteTrackingActive) return false\n if (!warnedDoubleCount) {\n warnedDoubleCount = true\n console.warn(\n '[telemetry] startTrackPage()/stopTrackPage() SKIPPED to prevent ' +\n 'double-counting: this instance was initialized with ' +\n 'enableAutoRouteTracking, so the App Insights SDK already reports a ' +\n 'page view per navigation. Pass { enableAutoRouteTracking: false } to ' +\n 'useTelemetry() if you want to time and report page views manually.',\n )\n }\n return true\n }\n\n const startTrackPage = (name?: string) => {\n // Redundancy first, for the same reason as trackPageView: it is a config\n // question, answered synchronously, and it must hold for calls made during the\n // lazy load too.\n if (manualPageTrackingIsRedundant()) return\n\n if (appInsights && isInitialized.value) {\n try {\n appInsights.startTrackPage(name)\n } catch (error) {\n console.error('Failed to start track page:', error)\n }\n return\n }\n\n // Time it ourselves while the SDK chunk is in flight — see preInitPageTimings.\n // Outside a load (initialize() never called, or it failed) this drops silently,\n // exactly as before.\n if (initPromise) {\n preInitPageTimings.set(resolvePageName(name), nowMs())\n }\n }\n\n const stopTrackPage = (\n name?: string,\n url?: string,\n properties?: Record<string, string>,\n measurements?: Record<string, number>,\n ) => {\n if (manualPageTrackingIsRedundant()) return\n\n if (appInsights && isInitialized.value) {\n try {\n appInsights.stopTrackPage(name, url, buildEventProperties(properties), measurements)\n } catch (error) {\n console.error('Failed to stop track page:', error)\n }\n return\n }\n\n if (!initPromise) return\n\n const key = resolvePageName(name)\n const startedAt = preInitPageTimings.get(key)\n // No matching start means there is no timing to report — and inventing one\n // would be worse than dropping it.\n if (startedAt === undefined) return\n preInitPageTimings.delete(key)\n\n const duration = Math.max(0, Math.round(nowMs() - startedAt))\n const payload = {\n name: key,\n uri: url ?? (typeof window !== 'undefined' ? window.location.href : undefined),\n // `duration` as a STRING alongside the custom properties is the SDK's OWN\n // encoding for a timed page view — `_pageTracking.action` does\n // `properties.duration = duration.toString()` — and it is what\n // `sendPageViewInternal` reads to back-date `startTime`.\n //\n // The cast is deliberate: `IPageViewTelemetry` declares `properties.duration`\n // as `number`, which its own `stopTrackPage` path does not honour. Matching\n // the runtime the collector actually parses beats matching a type that the\n // emitting SDK contradicts.\n properties: { ...buildEventProperties(properties), duration: String(duration) } as unknown as Record<\n string,\n number\n >,\n measurements,\n }\n\n bufferWhileLoading((instance) => instance.trackPageView(payload))\n }\n\n return {\n isEnabled,\n isInitialized,\n initializationError,\n initialize,\n whenReady,\n flush,\n startTrackPage,\n stopTrackPage,\n trackDependency,\n trackEvent,\n trackException,\n trackMetric,\n trackPageView,\n trackCtaClick,\n }\n}\n\n/** The full public API returned by {@link useTelemetry}. */\nexport type TelemetryApi = ReturnType<typeof useTelemetry>\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@duffcloudservices/telemetry",
3
- "version": "0.2.1",
4
- "description": "Shared Azure Application Insights telemetry for DCS Vue sites — one App Insights config (with the bfcache-safe unload fix) and a useTelemetry composable.",
3
+ "version": "0.3.0",
4
+ "description": "Shared Azure Application Insights telemetry for DCS Vue sites — one App Insights config (with the bfcache-safe unload fix) and a useTelemetry composable that loads the App Insights SDK lazily, off the consumer's entry chunk.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "//exports": [
@@ -46,6 +46,27 @@ vi.spyOn(console, 'error').mockImplementation(() => {})
46
46
  const CONNECTION_STRING =
47
47
  'InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://example.in.applicationinsights.azure.com/'
48
48
 
49
+ /**
50
+ * Call `initialize()` and let the LAZY SDK import settle (C-601).
51
+ *
52
+ * The SDK arrives through a dynamic `import()` now, so `initialize()` returns
53
+ * before `new ApplicationInsights(...)` has run. These specs are about the config
54
+ * and the double-count guards, not about the load window, so they wait once here
55
+ * and stay otherwise unchanged. The load window itself — buffering, ordering,
56
+ * single-init, and the no-static-import guard — is pinned by `lazySdk.test.ts`.
57
+ *
58
+ * Returns whatever `initialize()` returned, so the specs keep asserting on the
59
+ * unchanged synchronous `boolean` contract.
60
+ */
61
+ async function initAndAwaitSdk(t: {
62
+ initialize: () => boolean
63
+ whenReady: () => Promise<boolean>
64
+ }): Promise<boolean> {
65
+ const started = t.initialize()
66
+ await t.whenReady()
67
+ return started
68
+ }
69
+
49
70
  describe('useTelemetry', () => {
50
71
  beforeEach(() => {
51
72
  trackEventMock.mockReset()
@@ -65,7 +86,7 @@ describe('useTelemetry', () => {
65
86
  const t = useTelemetry()
66
87
 
67
88
  expect(t.isEnabled.value).toBe(false)
68
- expect(t.initialize()).toBe(false)
89
+ expect(await initAndAwaitSdk(t)).toBe(false)
69
90
  expect(warnSpy).toHaveBeenCalledWith(
70
91
  expect.stringContaining('Application Insights DISABLED'),
71
92
  )
@@ -79,7 +100,7 @@ describe('useTelemetry', () => {
79
100
  const { useTelemetry } = await import('./composable')
80
101
  const t = useTelemetry({ connectionString: CONNECTION_STRING, cloudRole: 'dcs-web' })
81
102
 
82
- expect(t.initialize()).toBe(true)
103
+ expect(await initAndAwaitSdk(t)).toBe(true)
83
104
  expect(t.isInitialized.value).toBe(true)
84
105
  expect(loadAppInsightsMock).toHaveBeenCalledTimes(1)
85
106
 
@@ -91,6 +112,58 @@ describe('useTelemetry', () => {
91
112
  expect(config.disablePageUnloadEvents).toEqual(['unload'])
92
113
  })
93
114
 
115
+ it('declines and constructs no SDK during a CI snapshot-capture pass (C-614)', async () => {
116
+ // The post-deploy capture step drives real Chromium against the LIVE
117
+ // production URL, so without this the marketing site records a pageView for
118
+ // every page its own deploy walked. Asserting on `constructorConfigs` rather
119
+ // than only the return value is deliberate: the requirement is that no SDK
120
+ // is built and no beacon can open, not merely that a boolean came back false.
121
+ const original = window.location
122
+ Object.defineProperty(window, 'location', {
123
+ configurable: true,
124
+ value: { hostname: 'duffcloudservices.com', pathname: '/', search: '?dcs-hide-ribbon=' },
125
+ })
126
+
127
+ try {
128
+ const { useTelemetry } = await import('./composable')
129
+ const t = useTelemetry({ connectionString: CONNECTION_STRING, cloudRole: 'dcs-web' })
130
+
131
+ expect(await initAndAwaitSdk(t)).toBe(false)
132
+ expect(t.isInitialized.value).toBe(false)
133
+ expect(loadAppInsightsMock).not.toHaveBeenCalled()
134
+ expect(constructorConfigs).toHaveLength(0)
135
+
136
+ // Every downstream emitter must stay silent too, not just the boot.
137
+ t.trackCtaClick('hero-cta', '/contact')
138
+ expect(trackEventMock).not.toHaveBeenCalled()
139
+ expect(trackPageViewMock).not.toHaveBeenCalled()
140
+ } finally {
141
+ Object.defineProperty(window, 'location', { configurable: true, value: original })
142
+ }
143
+ })
144
+
145
+ it('still initializes for a real visit carrying campaign parameters (C-614)', async () => {
146
+ // The positive control for the case above. Suppression that over-reaches
147
+ // would delete real visits invisibly, so this pins the boundary from the
148
+ // other side — and proves the test above is measuring the marker, not a
149
+ // location stub that happens to break initialization.
150
+ const original = window.location
151
+ Object.defineProperty(window, 'location', {
152
+ configurable: true,
153
+ value: { hostname: 'duffcloudservices.com', pathname: '/', search: '?utm_source=google' },
154
+ })
155
+
156
+ try {
157
+ const { useTelemetry } = await import('./composable')
158
+ const t = useTelemetry({ connectionString: CONNECTION_STRING, cloudRole: 'dcs-web' })
159
+
160
+ expect(await initAndAwaitSdk(t)).toBe(true)
161
+ expect(loadAppInsightsMock).toHaveBeenCalledTimes(1)
162
+ } finally {
163
+ Object.defineProperty(window, 'location', { configurable: true, value: original })
164
+ }
165
+ })
166
+
94
167
  it('flows consumer options through to event properties (cloudRole → app_name)', async () => {
95
168
  const { useTelemetry } = await import('./composable')
96
169
  const t = useTelemetry({
@@ -99,7 +172,7 @@ describe('useTelemetry', () => {
99
172
  environment: 'production',
100
173
  appVersion: '9.9.9',
101
174
  })
102
- t.initialize()
175
+ await initAndAwaitSdk(t)
103
176
 
104
177
  t.trackCtaClick('hero-cta', '/contact')
105
178
 
@@ -118,7 +191,7 @@ describe('useTelemetry', () => {
118
191
  it('trackCtaClick falls back to window.location.pathname when page omitted', async () => {
119
192
  const { useTelemetry } = await import('./composable')
120
193
  const t = useTelemetry({ connectionString: CONNECTION_STRING })
121
- t.initialize()
194
+ await initAndAwaitSdk(t)
122
195
 
123
196
  t.trackCtaClick('footer-cta')
124
197
 
@@ -131,7 +204,7 @@ describe('useTelemetry', () => {
131
204
  it('defaults cloudRole to dcs-site when unspecified', async () => {
132
205
  const { useTelemetry } = await import('./composable')
133
206
  const t = useTelemetry({ connectionString: CONNECTION_STRING })
134
- t.initialize()
207
+ await initAndAwaitSdk(t)
135
208
  t.trackEvent({ name: 'ping' })
136
209
  expect(trackEventMock.mock.calls[0][0].properties.app_name).toBe('dcs-site')
137
210
  })
@@ -148,7 +221,7 @@ describe('useTelemetry', () => {
148
221
  connectionString: CONNECTION_STRING,
149
222
  enableAutoRouteTracking: true,
150
223
  })
151
- t.initialize()
224
+ await initAndAwaitSdk(t)
152
225
 
153
226
  t.trackPageView()
154
227
  t.trackPageView({ name: 'Services' })
@@ -165,7 +238,7 @@ describe('useTelemetry', () => {
165
238
  connectionString: CONNECTION_STRING,
166
239
  enableAutoRouteTracking: true,
167
240
  })
168
- t.initialize()
241
+ await initAndAwaitSdk(t)
169
242
 
170
243
  t.trackPageView()
171
244
  t.trackPageView()
@@ -179,7 +252,7 @@ describe('useTelemetry', () => {
179
252
  it('defaults to auto-route tracking, so an unconfigured consumer cannot double-count', async () => {
180
253
  const { useTelemetry } = await import('./composable')
181
254
  const t = useTelemetry({ connectionString: CONNECTION_STRING })
182
- t.initialize()
255
+ await initAndAwaitSdk(t)
183
256
 
184
257
  t.trackPageView()
185
258
 
@@ -192,7 +265,7 @@ describe('useTelemetry', () => {
192
265
  connectionString: CONNECTION_STRING,
193
266
  enableAutoRouteTracking: false,
194
267
  })
195
- t.initialize()
268
+ await initAndAwaitSdk(t)
196
269
 
197
270
  t.trackPageView({ name: 'Services', uri: '/services' })
198
271
 
@@ -206,7 +279,7 @@ describe('useTelemetry', () => {
206
279
  connectionString: CONNECTION_STRING,
207
280
  enableAutoRouteTracking: true,
208
281
  })
209
- t.initialize()
282
+ await initAndAwaitSdk(t)
210
283
 
211
284
  t.trackPageView({ name: 'Hash route', force: true })
212
285
 
@@ -226,7 +299,7 @@ describe('useTelemetry', () => {
226
299
  connectionString: CONNECTION_STRING,
227
300
  enableAutoRouteTracking: true,
228
301
  })
229
- t.initialize()
302
+ await initAndAwaitSdk(t)
230
303
 
231
304
  t.startTrackPage('/contact')
232
305
  t.stopTrackPage('/contact', 'https://example.com/contact')
@@ -244,7 +317,7 @@ describe('useTelemetry', () => {
244
317
  connectionString: CONNECTION_STRING,
245
318
  enableAutoRouteTracking: false,
246
319
  })
247
- t.initialize()
320
+ await initAndAwaitSdk(t)
248
321
 
249
322
  t.startTrackPage('/contact')
250
323
  t.stopTrackPage('/contact', 'https://example.com/contact', { routeName: 'contact' })
@@ -263,7 +336,7 @@ describe('useTelemetry', () => {
263
336
  connectionString: CONNECTION_STRING,
264
337
  enableAutoRouteTracking: true,
265
338
  })
266
- t.initialize()
339
+ await initAndAwaitSdk(t)
267
340
 
268
341
  t.startTrackPage('/a')
269
342
  t.stopTrackPage('/a')
@@ -299,7 +372,7 @@ describe('useTelemetry', () => {
299
372
  conversionWindow().__dcsConversionAttach = attach
300
373
 
301
374
  const { useTelemetry } = await import('./composable')
302
- useTelemetry({ connectionString: CONNECTION_STRING }).initialize()
375
+ await initAndAwaitSdk(useTelemetry({ connectionString: CONNECTION_STRING }))
303
376
 
304
377
  expect(attach).toHaveBeenCalledTimes(1)
305
378
 
@@ -312,7 +385,7 @@ describe('useTelemetry', () => {
312
385
 
313
386
  it('is silent on a site with no cms conversion tracker', async () => {
314
387
  const { useTelemetry } = await import('./composable')
315
- const ok = useTelemetry({ connectionString: CONNECTION_STRING }).initialize()
388
+ const ok = await initAndAwaitSdk(useTelemetry({ connectionString: CONNECTION_STRING }))
316
389
 
317
390
  expect(ok).toBe(true)
318
391
  expect(warnSpy).not.toHaveBeenCalled()
@@ -324,7 +397,9 @@ describe('useTelemetry', () => {
324
397
  }
325
398
 
326
399
  const { useTelemetry } = await import('./composable')
327
- expect(useTelemetry({ connectionString: CONNECTION_STRING }).initialize()).toBe(true)
400
+ expect(
401
+ await initAndAwaitSdk(useTelemetry({ connectionString: CONNECTION_STRING })),
402
+ ).toBe(true)
328
403
  })
329
404
  })
330
405
  })