@plasius/nfr 1.0.0 → 1.0.2
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/README.md +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +17 -3
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
## Overview
|
|
14
14
|
|
|
15
|
-
`@plasius/nfr` provides
|
|
15
|
+
`@plasius/nfr` provides Non-Functional Requirement assistance, exposing platform agnostic Analytics, Performance Tracking and more.
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
package/dist/index.cjs
CHANGED
|
@@ -51,8 +51,8 @@ var trackPerf = (e) => {
|
|
|
51
51
|
};
|
|
52
52
|
var isProd = typeof import_meta !== "undefined" && import_meta.env?.MODE === "production" || process.env.NODE_ENV === "production";
|
|
53
53
|
var consoleSink = {
|
|
54
|
-
track: (event) => console.info(
|
|
55
|
-
page: (name, props) => console.info(
|
|
54
|
+
track: (event) => console.info(event.name, event.props),
|
|
55
|
+
page: (name, props) => console.info(name, props)
|
|
56
56
|
};
|
|
57
57
|
var dataLayerSink = {
|
|
58
58
|
track: (event) => {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/telemetry/analytics.ts","../src/telemetry/withInteractionTracking.tsx","../src/performance/webVitals.ts"],"sourcesContent":["export * from \"./telemetry/index.js\";\nexport * from \"./performance/index.js\";\n","import { PerfEvent } from \"../performance/index.js\";\n\nexport type AnalyticsEvent = {\n name: string;\n props?: Record<string, unknown>;\n ts?: number;\n};\n\nexport interface AnalyticsSink {\n track: (event: AnalyticsEvent) => void;\n page?: (name: string, props?: Record<string, unknown>) => void;\n}\n\n/**\n * Send a performance event through the active analytics sink.\n * We flatten the payload so `dataLayer`-style sinks can consume it directly.\n */\nexport const trackPerf = (e: PerfEvent) => {\n // Preserve provided timestamp if present; otherwise add one in the sink (or here)\n const { ts, ...rest } = e;\n activeSink.track({\n name: \"perf\",\n props: rest,\n ts: ts ?? Date.now(),\n });\n};\n\nconst isProd =\n (typeof import.meta !== \"undefined\" &&\n (import.meta as any).env?.MODE === \"production\") ||\n process.env.NODE_ENV === \"production\";\n\n// Example sinks\nconst consoleSink: AnalyticsSink = {\n track: (event) => console.info(\"[analytics:track]\", event),\n page: (name, props) => console.info(\"[analytics:page]\", { name, props }),\n};\n\n// Replace with your real one later:\nconst dataLayerSink: AnalyticsSink = {\n track: (event) => {\n // Example: Google Tag Manager dataLayer\n (window as any).dataLayer = (window as any).dataLayer || [];\n (window as any).dataLayer.push({\n event: event.name,\n ...event.props,\n ts: event.ts ?? Date.now(),\n });\n },\n page: (name, props) => {\n (window as any).dataLayer = (window as any).dataLayer || [];\n (window as any).dataLayer.push({\n event: \"page_view\",\n page: name,\n ...props,\n });\n },\n};\n\nlet activeSink: AnalyticsSink = isProd ? dataLayerSink : consoleSink;\n\nexport const setAnalyticsSink = (sink: AnalyticsSink) => {\n activeSink = sink;\n};\n\nexport const track = (name: string, props?: Record<string, unknown>) =>\n activeSink.track({ name, props, ts: Date.now() });\n\nexport const page = (name: string, props?: Record<string, unknown>) =>\n activeSink.page?.(name, props);\n","import React from \"react\";\nimport { track } from \"./analytics.js\";\n\ntype WithInteractionOpts = {\n origin?: string; // e.g. story name\n};\n\nexport function withInteractionTracking<P extends object, R = unknown>(\n Component: React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n >,\n opts?: WithInteractionOpts\n): React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n>;\n\nexport function withInteractionTracking<P extends object>(\n Component: React.ComponentType<P>,\n opts?: WithInteractionOpts\n): React.FC<P>;\n\nexport function withInteractionTracking<P extends object, R = unknown>(\n Component:\n | React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n >\n | React.ComponentType<P>,\n { origin }: WithInteractionOpts = {}\n): any {\n const Wrapped = React.forwardRef<R, P>(function Wrapped(props, ref) {\n const onInteractionCapture = (\n e: React.PointerEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>\n ) => {\n // Normalize event type\n const isKey = (e as React.KeyboardEvent).key !== undefined;\n const isPointer = (e as React.PointerEvent).pointerType !== undefined;\n const type = isKey ? \"keydown\" : isPointer ? \"pointerup\" : e.type;\n\n // Only track meaningful keyboard activations (Enter/Space)\n let key: string | undefined;\n if (isKey) {\n key = (e as React.KeyboardEvent).key;\n if (key !== \"Enter\" && key !== \" \") return; // ignore other keys\n }\n\n // For pointers: only primary button releases\n if (isPointer) {\n const pe = e as React.PointerEvent;\n if (type === \"pointerup\" && pe.button !== 0) return;\n }\n\n const target = e.target as HTMLElement | null;\n const label =\n target?.getAttribute?.(\"aria-label\") ||\n target?.getAttribute?.(\"data-analytics-label\") ||\n target?.textContent?.trim() ||\n target?.tagName;\n\n track(\"ui.interaction\", {\n origin,\n label,\n type,\n key,\n pointerType: isPointer ? (e as React.PointerEvent).pointerType : undefined,\n });\n };\n\n const Comp = Component as any; // Allow ref passing for both ref-aware and plain components\n return (\n <div\n onPointerUpCapture={onInteractionCapture}\n onKeyDownCapture={onInteractionCapture}\n >\n <Comp ref={ref} {...(props as any)} />\n </div>\n );\n });\n\n Wrapped.displayName = `WithInteractionTracking(${Component.displayName || Component.name || \"Component\"})`;\n return Wrapped;\n}\n","/*\n * Performance & Web Vitals tracking\n * ---------------------------------\n * A lightweight collector that wires up:\n * - Core Web Vitals (LCP, INP, CLS, FCP, TTFB) via optional dynamic import of `web-vitals`\n * - Long tasks via PerformanceObserver('longtask')\n * - Navigation timing (TTFB, DOMContentLoaded, load, etc.)\n * - Paint timings (FP, FCP fallback)\n * - Resource timings (optionally sampled)\n * - Visibility lifecycle (hidden, pagehide)\n * - Network info (downlink, rtt, effectiveType) when available\n * - Memory snapshot (JS heap) when available (Chromium only)\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport type VitalName = \"LCP\" | \"INP\" | \"CLS\" | \"FCP\" | \"TTFB\";\nexport type PerfCategory =\n | \"web-vitals\"\n | \"nav-timing\"\n | \"paint\"\n | \"resource\"\n | \"longtask\"\n | \"visibility\"\n | \"snapshot\";\n\nexport type PerfEvent = {\n category: PerfCategory;\n name: string; // e.g., \"LCP\", \"DOMContentLoaded\", \"resource:script\"\n value?: number; // ms for most; CLS is unitless\n rating?: \"good\" | \"needs-improvement\" | \"poor\";\n delta?: number; // for web-vitals deltas\n id?: string; // web-vitals id\n url?: string; // current page\n navigationType?: string; // navigate, reload, back_forward, prerender\n ts?: number; // epoch ms when measured\n details?: Record<string, any>;\n};\n\nexport type PerfTracker = (event: PerfEvent) => void;\n\nexport type InitOptions = {\n track: PerfTracker;\n /** Sample a subset of resource entries to avoid high-volume beacons */\n resourceSampleRate?: number; // 0..1, default 0.25\n /** Predicate to include a resource entry */\n resourceFilter?: (e: PerformanceResourceTiming) => boolean;\n /** Include network information (effectiveType/downlink/rtt) snapshots */\n includeNetworkInfo?: boolean;\n /** Include JS heap memory snapshot where supported */\n includeMemorySnapshot?: boolean;\n};\n\nconst now = () => Date.now();\nconst pageURL = () => (typeof location !== \"undefined\" ? location.href : undefined);\nconst navType = () => {\n try {\n const nav = performance.getEntriesByType(\"navigation\")[0] as PerformanceNavigationTiming | undefined;\n return nav?.type ?? (performance as any).navigation?.type;\n } catch {\n return undefined;\n }\n};\n\n// ---------------------------------------------------------------------------\n// Web Vitals (via optional dynamic import of `web-vitals`)\n// ---------------------------------------------------------------------------\nasync function wireWebVitals(track: PerfTracker) {\n try {\n // @ts-ignore - optional dependency; we ignore TS resolution and rely on runtime try/catch\n const mod: any = await import(/* webpackChunkName: \"web-vitals\" */ \"web-vitals\");\n const handlers: [string, (onReport: any, opts?: any) => void, any?][] = [\n [\"LCP\", mod.onLCP],\n [\"INP\", mod.onINP],\n [\"CLS\", mod.onCLS],\n [\"FCP\", mod.onFCP],\n [\"TTFB\", mod.onTTFB],\n ];\n\n handlers.forEach(([name, fn]) => {\n if (typeof fn === \"function\") {\n fn((metric: any) => {\n const { value, rating, delta, id, navigationType: nt, attribution } = metric;\n track({\n category: \"web-vitals\",\n name,\n value,\n rating,\n delta,\n id,\n url: pageURL(),\n navigationType: nt ?? navType(),\n ts: now(),\n details: attribution ? { attribution } : undefined,\n });\n }, { reportAllChanges: true });\n }\n });\n } catch {\n // `web-vitals` not available — silently skip.\n }\n}\n\n// ---------------------------------------------------------------------------\n// Navigation timing & paint timings\n// ---------------------------------------------------------------------------\nfunction reportNavigationTiming(track: PerfTracker) {\n try {\n const nav = performance.getEntriesByType(\"navigation\")[0] as PerformanceNavigationTiming | undefined;\n if (!nav) return;\n const base = {\n category: \"nav-timing\" as const,\n url: pageURL(),\n navigationType: nav.type,\n ts: now(),\n };\n const push = (name: string, value: number | undefined, details?: any) => {\n if (value == null || Number.isNaN(value)) return;\n track({ ...base, name, value, details });\n };\n\n // Common derived timings (ms)\n push(\"TTFB\", nav.responseStart);\n push(\"DNS\", nav.domainLookupEnd - nav.domainLookupStart);\n push(\"TCP\", nav.connectEnd - nav.connectStart);\n push(\"TLS\", nav.secureConnectionStart > 0 ? nav.connectEnd - nav.secureConnectionStart : 0);\n push(\"Request\", nav.responseStart - nav.requestStart);\n push(\"Response\", nav.responseEnd - nav.responseStart);\n push(\"DOMInteractive\", nav.domInteractive);\n push(\"DOMComplete\", nav.domComplete);\n push(\"DOMContentLoaded\", nav.domContentLoadedEventEnd);\n push(\"LoadEvent\", nav.loadEventEnd - nav.loadEventStart);\n push(\"FirstByteToInteractive\", nav.domInteractive - nav.responseStart);\n } catch {\n // ignore\n }\n}\n\nfunction reportPaintTimings(track: PerfTracker) {\n try {\n const paints = performance.getEntriesByType(\"paint\") as PerformanceEntry[];\n const base = { category: \"paint\" as const, url: pageURL(), navigationType: navType(), ts: now() };\n for (const p of paints) {\n track({ ...base, name: p.name, value: p.startTime });\n }\n } catch {\n // ignore\n }\n}\n\n// ---------------------------------------------------------------------------\n// Long tasks\n// ---------------------------------------------------------------------------\nfunction observeLongTasks(track: PerfTracker): () => void {\n if (!(\"PerformanceObserver\" in window)) return () => {};\n let obs: PerformanceObserver | undefined;\n try {\n obs = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n const anyEntry: any = entry as any;\n track({\n category: \"longtask\",\n name: \"longtask\",\n value: entry.duration,\n ts: now(),\n url: pageURL(),\n details: {\n startTime: entry.startTime,\n duration: entry.duration,\n attribution: anyEntry.attribution ?? anyEntry.attribution ?? undefined,\n },\n });\n }\n });\n obs.observe({ type: \"longtask\", buffered: true as any });\n } catch {\n // ignore\n }\n return () => {\n try { obs?.disconnect(); } catch {}\n };\n}\n\n// ---------------------------------------------------------------------------\n// Resource timings (sampled)\n// ---------------------------------------------------------------------------\nfunction observeResources(track: PerfTracker, sampleRate: number, filter?: (e: PerformanceResourceTiming) => boolean): () => void {\n if (!(\"PerformanceObserver\" in window)) return () => {};\n const shouldSample = (Math.max(0, Math.min(1, sampleRate)) || 0.25);\n const take = () => Math.random() < shouldSample;\n let obs: PerformanceObserver | undefined;\n\n const toEvent = (e: PerformanceResourceTiming): PerfEvent => ({\n category: \"resource\",\n name: `resource:${e.initiatorType || \"other\"}`,\n url: e.name,\n ts: now(),\n value: e.duration,\n details: {\n startTime: e.startTime,\n transferSize: (e as any).transferSize,\n encodedBodySize: (e as any).encodedBodySize,\n decodedBodySize: (e as any).decodedBodySize,\n nextHopProtocol: (e as any).nextHopProtocol,\n initiatorType: e.initiatorType,\n },\n });\n\n try {\n obs = new PerformanceObserver((list) => {\n for (const entry of list.getEntries() as PerformanceResourceTiming[]) {\n if (filter && !filter(entry)) continue;\n if (!take()) continue;\n track(toEvent(entry));\n }\n });\n obs.observe({ type: \"resource\", buffered: true as any });\n } catch {\n // ignore\n }\n return () => { try { obs?.disconnect(); } catch {} };\n}\n\n// ---------------------------------------------------------------------------\n// Visibility & lifecycle\n// ---------------------------------------------------------------------------\nfunction wireVisibility(track: PerfTracker): () => void {\n const onHidden = (type: string) => () => {\n track({ category: \"visibility\", name: type, ts: now(), url: pageURL() });\n };\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") onHidden(\"hidden\")();\n });\n window.addEventListener(\"pagehide\", onHidden(\"pagehide\"));\n return () => {\n document.removeEventListener(\"visibilitychange\", onHidden(\"hidden\"));\n window.removeEventListener(\"pagehide\", onHidden(\"pagehide\"));\n };\n}\n\n// ---------------------------------------------------------------------------\n// Snapshots: Network / Memory\n// ---------------------------------------------------------------------------\nfunction snapshotNetwork(track: PerfTracker) {\n const nav: any = (navigator as any);\n const c = nav?.connection || nav?.mozConnection || nav?.webkitConnection;\n if (!c) return;\n track({\n category: \"snapshot\",\n name: \"network-info\",\n ts: now(),\n url: pageURL(),\n details: {\n effectiveType: c.effectiveType,\n downlink: c.downlink,\n rtt: c.rtt,\n saveData: c.saveData,\n },\n });\n}\n\nfunction snapshotMemory(track: PerfTracker) {\n const mem: any = (performance as any).memory;\n if (!mem) return;\n track({\n category: \"snapshot\",\n name: \"js-heap\",\n ts: now(),\n url: pageURL(),\n details: {\n jsHeapSizeLimit: mem.jsHeapSizeLimit,\n totalJSHeapSize: mem.totalJSHeapSize,\n usedJSHeapSize: mem.usedJSHeapSize,\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\nexport function initPerformanceTracking(opts: InitOptions): () => void {\n const {\n track,\n resourceSampleRate = 0.25,\n resourceFilter,\n includeNetworkInfo = true,\n includeMemorySnapshot = false,\n } = opts;\n\n // Web Vitals (async)\n void wireWebVitals(track);\n\n // Navigation & paints (buffered entries are available after DOM ready)\n if (document.readyState === \"complete\") {\n reportNavigationTiming(track);\n reportPaintTimings(track);\n } else {\n window.addEventListener(\"load\", () => {\n reportNavigationTiming(track);\n reportPaintTimings(track);\n }, { once: true });\n }\n\n // Observers\n const offLong = observeLongTasks(track);\n const offRes = observeResources(track, resourceSampleRate, resourceFilter);\n const offVis = wireVisibility(track);\n\n // Snapshots\n if (includeNetworkInfo) snapshotNetwork(track);\n if (includeMemorySnapshot) snapshotMemory(track);\n\n // Teardown\n return () => {\n offLong();\n offRes();\n offVis();\n };\n}\n\nexport default {\n initPerformanceTracking,\n};\n"],"mappings":"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","names":["React","Wrapped","track"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/telemetry/analytics.ts","../src/telemetry/withInteractionTracking.tsx","../src/performance/webVitals.ts"],"sourcesContent":["export * from \"./telemetry/index.js\";\nexport * from \"./performance/index.js\";\n","import { PerfEvent } from \"../performance/index.js\";\n\nexport type AnalyticsEvent = {\n name: string;\n props?: Record<string, unknown>;\n ts?: number;\n};\n\nexport interface AnalyticsSink {\n track: (event: AnalyticsEvent) => void;\n page?: (name: string, props?: Record<string, unknown>) => void;\n}\n\n/**\n * Send a performance event through the active analytics sink.\n * We flatten the payload so `dataLayer`-style sinks can consume it directly.\n */\nexport const trackPerf = (e: PerfEvent) => {\n // Preserve provided timestamp if present; otherwise add one in the sink (or here)\n const { ts, ...rest } = e;\n activeSink.track({\n name: \"perf\",\n props: rest,\n ts: ts ?? Date.now(),\n });\n};\n\nconst isProd =\n (typeof import.meta !== \"undefined\" &&\n (import.meta as any).env?.MODE === \"production\") ||\n process.env.NODE_ENV === \"production\";\n\n// Example sinks\nconst consoleSink: AnalyticsSink = {\n track: (event) => console.info(event.name, event.props),\n page: (name, props) => console.info(name, props),\n};\n\n// Replace with your real one later:\nconst dataLayerSink: AnalyticsSink = {\n track: (event) => {\n // Example: Google Tag Manager dataLayer\n (window as any).dataLayer = (window as any).dataLayer || [];\n (window as any).dataLayer.push({\n event: event.name,\n ...event.props,\n ts: event.ts ?? Date.now(),\n });\n },\n page: (name, props) => {\n (window as any).dataLayer = (window as any).dataLayer || [];\n (window as any).dataLayer.push({\n event: \"page_view\",\n page: name,\n ...props,\n });\n },\n};\n\nlet activeSink: AnalyticsSink = isProd ? dataLayerSink : consoleSink;\n\nexport const setAnalyticsSink = (sink: AnalyticsSink) => {\n activeSink = sink;\n};\n\nexport const track = (name: string, props?: Record<string, unknown>) =>\n activeSink.track({ name, props, ts: Date.now() });\n\nexport const page = (name: string, props?: Record<string, unknown>) =>\n activeSink.page?.(name, props);\n","import React from \"react\";\nimport { track } from \"./analytics.js\";\n\ntype WithInteractionOpts = {\n origin?: string; // e.g. story name\n};\n\nexport function withInteractionTracking<P extends object, R = unknown>(\n Component: React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n >,\n opts?: WithInteractionOpts\n): React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n>;\n\nexport function withInteractionTracking<P extends object>(\n Component: React.ComponentType<P>,\n opts?: WithInteractionOpts\n): React.FC<P>;\n\nexport function withInteractionTracking<P extends object, R = unknown>(\n Component:\n | React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n >\n | React.ComponentType<P>,\n { origin }: WithInteractionOpts = {}\n): any {\n const Wrapped = React.forwardRef<R, P>(function Wrapped(props, ref) {\n const onInteractionCapture = (\n e: React.PointerEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>\n ) => {\n // Normalize event type\n const isKey = (e as React.KeyboardEvent).key !== undefined;\n const isPointer = (e as React.PointerEvent).pointerType !== undefined;\n const type = isKey ? \"keydown\" : isPointer ? \"pointerup\" : e.type;\n\n // Only track meaningful keyboard activations (Enter/Space)\n let key: string | undefined;\n if (isKey) {\n key = (e as React.KeyboardEvent).key;\n if (key !== \"Enter\" && key !== \" \") return; // ignore other keys\n }\n\n // For pointers: only primary button releases\n if (isPointer) {\n const pe = e as React.PointerEvent;\n if (type === \"pointerup\" && pe.button !== 0) return;\n }\n\n const target = e.target as HTMLElement | null;\n const label =\n target?.getAttribute?.(\"aria-label\") ||\n target?.getAttribute?.(\"data-analytics-label\") ||\n target?.textContent?.trim() ||\n target?.tagName;\n\n track(\"ui.interaction\", {\n origin,\n label,\n type,\n key,\n pointerType: isPointer ? (e as React.PointerEvent).pointerType : undefined,\n });\n };\n\n const Comp = Component as any; // Allow ref passing for both ref-aware and plain components\n return (\n <div\n onPointerUpCapture={onInteractionCapture}\n onKeyDownCapture={onInteractionCapture}\n >\n <Comp ref={ref} {...(props as any)} />\n </div>\n );\n });\n\n Wrapped.displayName = `WithInteractionTracking(${Component.displayName || Component.name || \"Component\"})`;\n return Wrapped;\n}\n","/*\n * Performance & Web Vitals tracking\n * ---------------------------------\n * A lightweight collector that wires up:\n * - Core Web Vitals (LCP, INP, CLS, FCP, TTFB) via optional dynamic import of `web-vitals`\n * - Long tasks via PerformanceObserver('longtask')\n * - Navigation timing (TTFB, DOMContentLoaded, load, etc.)\n * - Paint timings (FP, FCP fallback)\n * - Resource timings (optionally sampled)\n * - Visibility lifecycle (hidden, pagehide)\n * - Network info (downlink, rtt, effectiveType) when available\n * - Memory snapshot (JS heap) when available (Chromium only)\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport type VitalName = \"LCP\" | \"INP\" | \"CLS\" | \"FCP\" | \"TTFB\";\nexport type PerfCategory =\n | \"web-vitals\"\n | \"nav-timing\"\n | \"paint\"\n | \"resource\"\n | \"longtask\"\n | \"visibility\"\n | \"snapshot\";\n\nexport type PerfEvent = {\n category: PerfCategory;\n name: string; // e.g., \"LCP\", \"DOMContentLoaded\", \"resource:script\"\n value?: number; // ms for most; CLS is unitless\n rating?: \"good\" | \"needs-improvement\" | \"poor\";\n delta?: number; // for web-vitals deltas\n id?: string; // web-vitals id\n url?: string; // current page\n navigationType?: string; // navigate, reload, back_forward, prerender\n ts?: number; // epoch ms when measured\n details?: Record<string, any>;\n};\n\nexport type PerfTracker = (event: PerfEvent) => void;\n\nexport type InitOptions = {\n track: PerfTracker;\n /** Sample a subset of resource entries to avoid high-volume beacons */\n resourceSampleRate?: number; // 0..1, default 0.25\n /** Predicate to include a resource entry */\n resourceFilter?: (e: PerformanceResourceTiming) => boolean;\n /** Include network information (effectiveType/downlink/rtt) snapshots */\n includeNetworkInfo?: boolean;\n /** Include JS heap memory snapshot where supported */\n includeMemorySnapshot?: boolean;\n};\n\nconst now = () => Date.now();\nconst pageURL = () => (typeof location !== \"undefined\" ? location.href : undefined);\nconst navType = () => {\n try {\n const nav = performance.getEntriesByType(\"navigation\")[0] as PerformanceNavigationTiming | undefined;\n return nav?.type ?? (performance as any).navigation?.type;\n } catch {\n return undefined;\n }\n};\n\n// ---------------------------------------------------------------------------\n// Web Vitals (via optional dynamic import of `web-vitals`)\n// ---------------------------------------------------------------------------\nasync function wireWebVitals(track: PerfTracker) {\n try {\n // @ts-ignore - optional dependency; we ignore TS resolution and rely on runtime try/catch\n const mod: any = await import(/* webpackChunkName: \"web-vitals\" */ \"web-vitals\");\n const handlers: [string, (onReport: any, opts?: any) => void, any?][] = [\n [\"LCP\", mod.onLCP],\n [\"INP\", mod.onINP],\n [\"CLS\", mod.onCLS],\n [\"FCP\", mod.onFCP],\n [\"TTFB\", mod.onTTFB],\n ];\n\n handlers.forEach(([name, fn]) => {\n if (typeof fn === \"function\") {\n fn((metric: any) => {\n const { value, rating, delta, id, navigationType: nt, attribution } = metric;\n track({\n category: \"web-vitals\",\n name,\n value,\n rating,\n delta,\n id,\n url: pageURL(),\n navigationType: nt ?? navType(),\n ts: now(),\n details: attribution ? { attribution } : undefined,\n });\n }, { reportAllChanges: true });\n }\n });\n } catch {\n // `web-vitals` not available — silently skip.\n }\n}\n\n// ---------------------------------------------------------------------------\n// Navigation timing & paint timings\n// ---------------------------------------------------------------------------\nfunction reportNavigationTiming(track: PerfTracker) {\n try {\n const nav = performance.getEntriesByType(\"navigation\")[0] as PerformanceNavigationTiming | undefined;\n if (!nav) return;\n const base = {\n category: \"nav-timing\" as const,\n url: pageURL(),\n navigationType: nav.type,\n ts: now(),\n };\n const push = (name: string, value: number | undefined, details?: any) => {\n if (value == null || Number.isNaN(value)) return;\n track({ ...base, name, value, details });\n };\n\n // Common derived timings (ms)\n push(\"TTFB\", nav.responseStart);\n push(\"DNS\", nav.domainLookupEnd - nav.domainLookupStart);\n push(\"TCP\", nav.connectEnd - nav.connectStart);\n push(\"TLS\", nav.secureConnectionStart > 0 ? nav.connectEnd - nav.secureConnectionStart : 0);\n push(\"Request\", nav.responseStart - nav.requestStart);\n push(\"Response\", nav.responseEnd - nav.responseStart);\n push(\"DOMInteractive\", nav.domInteractive);\n push(\"DOMComplete\", nav.domComplete);\n push(\"DOMContentLoaded\", nav.domContentLoadedEventEnd);\n push(\"LoadEvent\", nav.loadEventEnd - nav.loadEventStart);\n push(\"FirstByteToInteractive\", nav.domInteractive - nav.responseStart);\n } catch {\n // ignore\n }\n}\n\nfunction reportPaintTimings(track: PerfTracker) {\n try {\n const paints = performance.getEntriesByType(\"paint\") as PerformanceEntry[];\n const base = { category: \"paint\" as const, url: pageURL(), navigationType: navType(), ts: now() };\n for (const p of paints) {\n track({ ...base, name: p.name, value: p.startTime });\n }\n } catch {\n // ignore\n }\n}\n\n// ---------------------------------------------------------------------------\n// Long tasks\n// ---------------------------------------------------------------------------\nfunction observeLongTasks(track: PerfTracker): () => void {\n if (!(\"PerformanceObserver\" in window)) return () => {};\n let obs: PerformanceObserver | undefined;\n try {\n obs = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n const anyEntry: any = entry as any;\n track({\n category: \"longtask\",\n name: \"longtask\",\n value: entry.duration,\n ts: now(),\n url: pageURL(),\n details: {\n startTime: entry.startTime,\n duration: entry.duration,\n attribution: anyEntry.attribution ?? anyEntry.attribution ?? undefined,\n },\n });\n }\n });\n obs.observe({ type: \"longtask\", buffered: true as any });\n } catch {\n // ignore\n }\n return () => {\n try { obs?.disconnect(); } catch {}\n };\n}\n\n// ---------------------------------------------------------------------------\n// Resource timings (sampled)\n// ---------------------------------------------------------------------------\nfunction observeResources(track: PerfTracker, sampleRate: number, filter?: (e: PerformanceResourceTiming) => boolean): () => void {\n if (!(\"PerformanceObserver\" in window)) return () => {};\n const shouldSample = (Math.max(0, Math.min(1, sampleRate)) || 0.25);\n const take = () => Math.random() < shouldSample;\n let obs: PerformanceObserver | undefined;\n\n const toEvent = (e: PerformanceResourceTiming): PerfEvent => ({\n category: \"resource\",\n name: `resource:${e.initiatorType || \"other\"}`,\n url: e.name,\n ts: now(),\n value: e.duration,\n details: {\n startTime: e.startTime,\n transferSize: (e as any).transferSize,\n encodedBodySize: (e as any).encodedBodySize,\n decodedBodySize: (e as any).decodedBodySize,\n nextHopProtocol: (e as any).nextHopProtocol,\n initiatorType: e.initiatorType,\n },\n });\n\n try {\n obs = new PerformanceObserver((list) => {\n for (const entry of list.getEntries() as PerformanceResourceTiming[]) {\n if (filter && !filter(entry)) continue;\n if (!take()) continue;\n track(toEvent(entry));\n }\n });\n obs.observe({ type: \"resource\", buffered: true as any });\n } catch {\n // ignore\n }\n return () => { try { obs?.disconnect(); } catch {} };\n}\n\n// ---------------------------------------------------------------------------\n// Visibility & lifecycle\n// ---------------------------------------------------------------------------\nfunction wireVisibility(track: PerfTracker): () => void {\n const onHidden = (type: string) => () => {\n track({ category: \"visibility\", name: type, ts: now(), url: pageURL() });\n };\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") onHidden(\"hidden\")();\n });\n window.addEventListener(\"pagehide\", onHidden(\"pagehide\"));\n return () => {\n document.removeEventListener(\"visibilitychange\", onHidden(\"hidden\"));\n window.removeEventListener(\"pagehide\", onHidden(\"pagehide\"));\n };\n}\n\n// ---------------------------------------------------------------------------\n// Snapshots: Network / Memory\n// ---------------------------------------------------------------------------\nfunction snapshotNetwork(track: PerfTracker) {\n const nav: any = (navigator as any);\n const c = nav?.connection || nav?.mozConnection || nav?.webkitConnection;\n if (!c) return;\n track({\n category: \"snapshot\",\n name: \"network-info\",\n ts: now(),\n url: pageURL(),\n details: {\n effectiveType: c.effectiveType,\n downlink: c.downlink,\n rtt: c.rtt,\n saveData: c.saveData,\n },\n });\n}\n\nfunction snapshotMemory(track: PerfTracker) {\n const mem: any = (performance as any).memory;\n if (!mem) return;\n track({\n category: \"snapshot\",\n name: \"js-heap\",\n ts: now(),\n url: pageURL(),\n details: {\n jsHeapSizeLimit: mem.jsHeapSizeLimit,\n totalJSHeapSize: mem.totalJSHeapSize,\n usedJSHeapSize: mem.usedJSHeapSize,\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\nexport function initPerformanceTracking(opts: InitOptions): () => void {\n const {\n track,\n resourceSampleRate = 0.25,\n resourceFilter,\n includeNetworkInfo = true,\n includeMemorySnapshot = false,\n } = opts;\n\n // Web Vitals (async)\n void wireWebVitals(track);\n\n // Navigation & paints (buffered entries are available after DOM ready)\n if (document.readyState === \"complete\") {\n reportNavigationTiming(track);\n reportPaintTimings(track);\n } else {\n window.addEventListener(\"load\", () => {\n reportNavigationTiming(track);\n reportPaintTimings(track);\n }, { once: true });\n }\n\n // Observers\n const offLong = observeLongTasks(track);\n const offRes = observeResources(track, resourceSampleRate, resourceFilter);\n const offVis = wireVisibility(track);\n\n // Snapshots\n if (includeNetworkInfo) snapshotNetwork(track);\n if (includeMemorySnapshot) snapshotMemory(track);\n\n // Teardown\n return () => {\n offLong();\n offRes();\n offVis();\n };\n}\n\nexport default {\n initPerformanceTracking,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAiBO,IAAM,YAAY,CAAC,MAAiB;AAEzC,QAAM,EAAE,IAAI,GAAG,KAAK,IAAI;AACxB,aAAW,MAAM;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,IAAI,MAAM,KAAK,IAAI;AAAA,EACrB,CAAC;AACH;AAEA,IAAM,SACH,OAAO,gBAAgB,eACrB,YAAoB,KAAK,SAAS,gBACrC,QAAQ,IAAI,aAAa;AAG3B,IAAM,cAA6B;AAAA,EACjC,OAAO,CAAC,UAAU,QAAQ,KAAK,MAAM,MAAM,MAAM,KAAK;AAAA,EACtD,MAAM,CAAC,MAAM,UAAU,QAAQ,KAAK,MAAM,KAAK;AACjD;AAGA,IAAM,gBAA+B;AAAA,EACnC,OAAO,CAAC,UAAU;AAEhB,IAAC,OAAe,YAAa,OAAe,aAAa,CAAC;AAC1D,IAAC,OAAe,UAAU,KAAK;AAAA,MAC7B,OAAO,MAAM;AAAA,MACb,GAAG,MAAM;AAAA,MACT,IAAI,MAAM,MAAM,KAAK,IAAI;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA,EACA,MAAM,CAAC,MAAM,UAAU;AACrB,IAAC,OAAe,YAAa,OAAe,aAAa,CAAC;AAC1D,IAAC,OAAe,UAAU,KAAK;AAAA,MAC7B,OAAO;AAAA,MACP,MAAM;AAAA,MACN,GAAG;AAAA,IACL,CAAC;AAAA,EACH;AACF;AAEA,IAAI,aAA4B,SAAS,gBAAgB;AAElD,IAAM,mBAAmB,CAAC,SAAwB;AACvD,eAAa;AACf;AAEO,IAAM,QAAQ,CAAC,MAAc,UAClC,WAAW,MAAM,EAAE,MAAM,OAAO,IAAI,KAAK,IAAI,EAAE,CAAC;AAE3C,IAAM,OAAO,CAAC,MAAc,UACjC,WAAW,OAAO,MAAM,KAAK;;;ACrE/B,mBAAkB;AAyEV;AApDD,SAAS,wBACd,WAKA,EAAE,OAAO,IAAyB,CAAC,GAC9B;AACL,QAAM,UAAU,aAAAA,QAAM,WAAiB,SAASC,SAAQ,OAAO,KAAK;AAClE,UAAM,uBAAuB,CAC3B,MACG;AAEH,YAAM,QAAS,EAA0B,QAAQ;AACjD,YAAM,YAAa,EAAyB,gBAAgB;AAC5D,YAAM,OAAO,QAAQ,YAAY,YAAY,cAAc,EAAE;AAG7D,UAAI;AACJ,UAAI,OAAO;AACT,cAAO,EAA0B;AACjC,YAAI,QAAQ,WAAW,QAAQ,IAAK;AAAA,MACtC;AAGA,UAAI,WAAW;AACb,cAAM,KAAK;AACX,YAAI,SAAS,eAAe,GAAG,WAAW,EAAG;AAAA,MAC/C;AAEA,YAAM,SAAS,EAAE;AACjB,YAAM,QACJ,QAAQ,eAAe,YAAY,KACnC,QAAQ,eAAe,sBAAsB,KAC7C,QAAQ,aAAa,KAAK,KAC1B,QAAQ;AAEV,YAAM,kBAAkB;AAAA,QACtB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,aAAa,YAAa,EAAyB,cAAc;AAAA,MACnE,CAAC;AAAA,IACH;AAEA,UAAM,OAAO;AACb,WACE;AAAA,MAAC;AAAA;AAAA,QACC,oBAAoB;AAAA,QACpB,kBAAkB;AAAA,QAElB,sDAAC,QAAK,KAAW,GAAI,OAAe;AAAA;AAAA,IACtC;AAAA,EAEJ,CAAC;AAED,UAAQ,cAAc,2BAA2B,UAAU,eAAe,UAAU,QAAQ,WAAW;AACvG,SAAO;AACT;;;AC3BA,IAAM,MAAM,MAAM,KAAK,IAAI;AAC3B,IAAM,UAAU,MAAO,OAAO,aAAa,cAAc,SAAS,OAAO;AACzE,IAAM,UAAU,MAAM;AACpB,MAAI;AACF,UAAM,MAAM,YAAY,iBAAiB,YAAY,EAAE,CAAC;AACxD,WAAO,KAAK,QAAS,YAAoB,YAAY;AAAA,EACvD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,eAAe,cAAcC,QAAoB;AAC/C,MAAI;AAEF,UAAM,MAAW,MAAM;AAAA;AAAA,MAA4C;AAAA,IAAY;AAC/E,UAAM,WAAkE;AAAA,MACtE,CAAC,OAAO,IAAI,KAAK;AAAA,MACjB,CAAC,OAAO,IAAI,KAAK;AAAA,MACjB,CAAC,OAAO,IAAI,KAAK;AAAA,MACjB,CAAC,OAAO,IAAI,KAAK;AAAA,MACjB,CAAC,QAAQ,IAAI,MAAM;AAAA,IACrB;AAEA,aAAS,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM;AAC/B,UAAI,OAAO,OAAO,YAAY;AAC5B,WAAG,CAAC,WAAgB;AAClB,gBAAM,EAAE,OAAO,QAAQ,OAAO,IAAI,gBAAgB,IAAI,YAAY,IAAI;AACtE,UAAAA,OAAM;AAAA,YACJ,UAAU;AAAA,YACV;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,KAAK,QAAQ;AAAA,YACb,gBAAgB,MAAM,QAAQ;AAAA,YAC9B,IAAI,IAAI;AAAA,YACR,SAAS,cAAc,EAAE,YAAY,IAAI;AAAA,UAC3C,CAAC;AAAA,QACH,GAAG,EAAE,kBAAkB,KAAK,CAAC;AAAA,MAC/B;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAKA,SAAS,uBAAuBA,QAAoB;AAClD,MAAI;AACF,UAAM,MAAM,YAAY,iBAAiB,YAAY,EAAE,CAAC;AACxD,QAAI,CAAC,IAAK;AACV,UAAM,OAAO;AAAA,MACX,UAAU;AAAA,MACV,KAAK,QAAQ;AAAA,MACb,gBAAgB,IAAI;AAAA,MACpB,IAAI,IAAI;AAAA,IACV;AACA,UAAM,OAAO,CAAC,MAAc,OAA2B,YAAkB;AACvE,UAAI,SAAS,QAAQ,OAAO,MAAM,KAAK,EAAG;AAC1C,MAAAA,OAAM,EAAE,GAAG,MAAM,MAAM,OAAO,QAAQ,CAAC;AAAA,IACzC;AAGA,SAAK,QAAQ,IAAI,aAAa;AAC9B,SAAK,OAAO,IAAI,kBAAkB,IAAI,iBAAiB;AACvD,SAAK,OAAO,IAAI,aAAa,IAAI,YAAY;AAC7C,SAAK,OAAO,IAAI,wBAAwB,IAAI,IAAI,aAAa,IAAI,wBAAwB,CAAC;AAC1F,SAAK,WAAW,IAAI,gBAAgB,IAAI,YAAY;AACpD,SAAK,YAAY,IAAI,cAAc,IAAI,aAAa;AACpD,SAAK,kBAAkB,IAAI,cAAc;AACzC,SAAK,eAAe,IAAI,WAAW;AACnC,SAAK,oBAAoB,IAAI,wBAAwB;AACrD,SAAK,aAAa,IAAI,eAAe,IAAI,cAAc;AACvD,SAAK,0BAA0B,IAAI,iBAAiB,IAAI,aAAa;AAAA,EACvE,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,mBAAmBA,QAAoB;AAC9C,MAAI;AACF,UAAM,SAAS,YAAY,iBAAiB,OAAO;AACnD,UAAM,OAAO,EAAE,UAAU,SAAkB,KAAK,QAAQ,GAAG,gBAAgB,QAAQ,GAAG,IAAI,IAAI,EAAE;AAChG,eAAW,KAAK,QAAQ;AACtB,MAAAA,OAAM,EAAE,GAAG,MAAM,MAAM,EAAE,MAAM,OAAO,EAAE,UAAU,CAAC;AAAA,IACrD;AAAA,EACF,QAAQ;AAAA,EAER;AACF;AAKA,SAAS,iBAAiBA,QAAgC;AACxD,MAAI,EAAE,yBAAyB,QAAS,QAAO,MAAM;AAAA,EAAC;AACtD,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,oBAAoB,CAAC,SAAS;AACtC,iBAAW,SAAS,KAAK,WAAW,GAAG;AACrC,cAAM,WAAgB;AACtB,QAAAA,OAAM;AAAA,UACJ,UAAU;AAAA,UACV,MAAM;AAAA,UACN,OAAO,MAAM;AAAA,UACb,IAAI,IAAI;AAAA,UACR,KAAK,QAAQ;AAAA,UACb,SAAS;AAAA,YACP,WAAW,MAAM;AAAA,YACjB,UAAU,MAAM;AAAA,YAChB,aAAa,SAAS,eAAe,SAAS,eAAe;AAAA,UAC/D;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AACD,QAAI,QAAQ,EAAE,MAAM,YAAY,UAAU,KAAY,CAAC;AAAA,EACzD,QAAQ;AAAA,EAER;AACA,SAAO,MAAM;AACX,QAAI;AAAE,WAAK,WAAW;AAAA,IAAG,QAAQ;AAAA,IAAC;AAAA,EACpC;AACF;AAKA,SAAS,iBAAiBA,QAAoB,YAAoB,QAAgE;AAChI,MAAI,EAAE,yBAAyB,QAAS,QAAO,MAAM;AAAA,EAAC;AACtD,QAAM,eAAgB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,UAAU,CAAC,KAAK;AAC9D,QAAM,OAAO,MAAM,KAAK,OAAO,IAAI;AACnC,MAAI;AAEJ,QAAM,UAAU,CAAC,OAA6C;AAAA,IAC5D,UAAU;AAAA,IACV,MAAM,YAAY,EAAE,iBAAiB,OAAO;AAAA,IAC5C,KAAK,EAAE;AAAA,IACP,IAAI,IAAI;AAAA,IACR,OAAO,EAAE;AAAA,IACT,SAAS;AAAA,MACP,WAAW,EAAE;AAAA,MACb,cAAe,EAAU;AAAA,MACzB,iBAAkB,EAAU;AAAA,MAC5B,iBAAkB,EAAU;AAAA,MAC5B,iBAAkB,EAAU;AAAA,MAC5B,eAAe,EAAE;AAAA,IACnB;AAAA,EACF;AAEA,MAAI;AACF,UAAM,IAAI,oBAAoB,CAAC,SAAS;AACtC,iBAAW,SAAS,KAAK,WAAW,GAAkC;AACpE,YAAI,UAAU,CAAC,OAAO,KAAK,EAAG;AAC9B,YAAI,CAAC,KAAK,EAAG;AACb,QAAAA,OAAM,QAAQ,KAAK,CAAC;AAAA,MACtB;AAAA,IACF,CAAC;AACD,QAAI,QAAQ,EAAE,MAAM,YAAY,UAAU,KAAY,CAAC;AAAA,EACzD,QAAQ;AAAA,EAER;AACA,SAAO,MAAM;AAAE,QAAI;AAAE,WAAK,WAAW;AAAA,IAAG,QAAQ;AAAA,IAAC;AAAA,EAAE;AACrD;AAKA,SAAS,eAAeA,QAAgC;AACtD,QAAM,WAAW,CAAC,SAAiB,MAAM;AACvC,IAAAA,OAAM,EAAE,UAAU,cAAc,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;AAAA,EACzE;AACA,WAAS,iBAAiB,oBAAoB,MAAM;AAClD,QAAI,SAAS,oBAAoB,SAAU,UAAS,QAAQ,EAAE;AAAA,EAChE,CAAC;AACD,SAAO,iBAAiB,YAAY,SAAS,UAAU,CAAC;AACxD,SAAO,MAAM;AACX,aAAS,oBAAoB,oBAAoB,SAAS,QAAQ,CAAC;AACnE,WAAO,oBAAoB,YAAY,SAAS,UAAU,CAAC;AAAA,EAC7D;AACF;AAKA,SAAS,gBAAgBA,QAAoB;AAC3C,QAAM,MAAY;AAClB,QAAM,IAAI,KAAK,cAAc,KAAK,iBAAiB,KAAK;AACxD,MAAI,CAAC,EAAG;AACR,EAAAA,OAAM;AAAA,IACJ,UAAU;AAAA,IACV,MAAM;AAAA,IACN,IAAI,IAAI;AAAA,IACR,KAAK,QAAQ;AAAA,IACb,SAAS;AAAA,MACP,eAAe,EAAE;AAAA,MACjB,UAAU,EAAE;AAAA,MACZ,KAAK,EAAE;AAAA,MACP,UAAU,EAAE;AAAA,IACd;AAAA,EACF,CAAC;AACH;AAEA,SAAS,eAAeA,QAAoB;AAC1C,QAAM,MAAY,YAAoB;AACtC,MAAI,CAAC,IAAK;AACV,EAAAA,OAAM;AAAA,IACJ,UAAU;AAAA,IACV,MAAM;AAAA,IACN,IAAI,IAAI;AAAA,IACR,KAAK,QAAQ;AAAA,IACb,SAAS;AAAA,MACP,iBAAiB,IAAI;AAAA,MACrB,iBAAiB,IAAI;AAAA,MACrB,gBAAgB,IAAI;AAAA,IACtB;AAAA,EACF,CAAC;AACH;AAKO,SAAS,wBAAwB,MAA+B;AACrE,QAAM;AAAA,IACJ,OAAAA;AAAA,IACA,qBAAqB;AAAA,IACrB;AAAA,IACA,qBAAqB;AAAA,IACrB,wBAAwB;AAAA,EAC1B,IAAI;AAGJ,OAAK,cAAcA,MAAK;AAGxB,MAAI,SAAS,eAAe,YAAY;AACtC,2BAAuBA,MAAK;AAC5B,uBAAmBA,MAAK;AAAA,EAC1B,OAAO;AACL,WAAO,iBAAiB,QAAQ,MAAM;AACpC,6BAAuBA,MAAK;AAC5B,yBAAmBA,MAAK;AAAA,IAC1B,GAAG,EAAE,MAAM,KAAK,CAAC;AAAA,EACnB;AAGA,QAAM,UAAU,iBAAiBA,MAAK;AACtC,QAAM,SAAS,iBAAiBA,QAAO,oBAAoB,cAAc;AACzE,QAAM,SAAS,eAAeA,MAAK;AAGnC,MAAI,mBAAoB,iBAAgBA,MAAK;AAC7C,MAAI,sBAAuB,gBAAeA,MAAK;AAG/C,SAAO,MAAM;AACX,YAAQ;AACR,WAAO;AACP,WAAO;AAAA,EACT;AACF;","names":["React","Wrapped","track"]}
|
package/dist/index.js
CHANGED
|
@@ -9,8 +9,8 @@ var trackPerf = (e) => {
|
|
|
9
9
|
};
|
|
10
10
|
var isProd = typeof import.meta !== "undefined" && import.meta.env?.MODE === "production" || process.env.NODE_ENV === "production";
|
|
11
11
|
var consoleSink = {
|
|
12
|
-
track: (event) => console.info(
|
|
13
|
-
page: (name, props) => console.info(
|
|
12
|
+
track: (event) => console.info(event.name, event.props),
|
|
13
|
+
page: (name, props) => console.info(name, props)
|
|
14
14
|
};
|
|
15
15
|
var dataLayerSink = {
|
|
16
16
|
track: (event) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/telemetry/analytics.ts","../src/telemetry/withInteractionTracking.tsx","../src/performance/webVitals.ts"],"sourcesContent":["import { PerfEvent } from \"../performance/index.js\";\n\nexport type AnalyticsEvent = {\n name: string;\n props?: Record<string, unknown>;\n ts?: number;\n};\n\nexport interface AnalyticsSink {\n track: (event: AnalyticsEvent) => void;\n page?: (name: string, props?: Record<string, unknown>) => void;\n}\n\n/**\n * Send a performance event through the active analytics sink.\n * We flatten the payload so `dataLayer`-style sinks can consume it directly.\n */\nexport const trackPerf = (e: PerfEvent) => {\n // Preserve provided timestamp if present; otherwise add one in the sink (or here)\n const { ts, ...rest } = e;\n activeSink.track({\n name: \"perf\",\n props: rest,\n ts: ts ?? Date.now(),\n });\n};\n\nconst isProd =\n (typeof import.meta !== \"undefined\" &&\n (import.meta as any).env?.MODE === \"production\") ||\n process.env.NODE_ENV === \"production\";\n\n// Example sinks\nconst consoleSink: AnalyticsSink = {\n track: (event) => console.info(\"[analytics:track]\", event),\n page: (name, props) => console.info(\"[analytics:page]\", { name, props }),\n};\n\n// Replace with your real one later:\nconst dataLayerSink: AnalyticsSink = {\n track: (event) => {\n // Example: Google Tag Manager dataLayer\n (window as any).dataLayer = (window as any).dataLayer || [];\n (window as any).dataLayer.push({\n event: event.name,\n ...event.props,\n ts: event.ts ?? Date.now(),\n });\n },\n page: (name, props) => {\n (window as any).dataLayer = (window as any).dataLayer || [];\n (window as any).dataLayer.push({\n event: \"page_view\",\n page: name,\n ...props,\n });\n },\n};\n\nlet activeSink: AnalyticsSink = isProd ? dataLayerSink : consoleSink;\n\nexport const setAnalyticsSink = (sink: AnalyticsSink) => {\n activeSink = sink;\n};\n\nexport const track = (name: string, props?: Record<string, unknown>) =>\n activeSink.track({ name, props, ts: Date.now() });\n\nexport const page = (name: string, props?: Record<string, unknown>) =>\n activeSink.page?.(name, props);\n","import React from \"react\";\nimport { track } from \"./analytics.js\";\n\ntype WithInteractionOpts = {\n origin?: string; // e.g. story name\n};\n\nexport function withInteractionTracking<P extends object, R = unknown>(\n Component: React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n >,\n opts?: WithInteractionOpts\n): React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n>;\n\nexport function withInteractionTracking<P extends object>(\n Component: React.ComponentType<P>,\n opts?: WithInteractionOpts\n): React.FC<P>;\n\nexport function withInteractionTracking<P extends object, R = unknown>(\n Component:\n | React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n >\n | React.ComponentType<P>,\n { origin }: WithInteractionOpts = {}\n): any {\n const Wrapped = React.forwardRef<R, P>(function Wrapped(props, ref) {\n const onInteractionCapture = (\n e: React.PointerEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>\n ) => {\n // Normalize event type\n const isKey = (e as React.KeyboardEvent).key !== undefined;\n const isPointer = (e as React.PointerEvent).pointerType !== undefined;\n const type = isKey ? \"keydown\" : isPointer ? \"pointerup\" : e.type;\n\n // Only track meaningful keyboard activations (Enter/Space)\n let key: string | undefined;\n if (isKey) {\n key = (e as React.KeyboardEvent).key;\n if (key !== \"Enter\" && key !== \" \") return; // ignore other keys\n }\n\n // For pointers: only primary button releases\n if (isPointer) {\n const pe = e as React.PointerEvent;\n if (type === \"pointerup\" && pe.button !== 0) return;\n }\n\n const target = e.target as HTMLElement | null;\n const label =\n target?.getAttribute?.(\"aria-label\") ||\n target?.getAttribute?.(\"data-analytics-label\") ||\n target?.textContent?.trim() ||\n target?.tagName;\n\n track(\"ui.interaction\", {\n origin,\n label,\n type,\n key,\n pointerType: isPointer ? (e as React.PointerEvent).pointerType : undefined,\n });\n };\n\n const Comp = Component as any; // Allow ref passing for both ref-aware and plain components\n return (\n <div\n onPointerUpCapture={onInteractionCapture}\n onKeyDownCapture={onInteractionCapture}\n >\n <Comp ref={ref} {...(props as any)} />\n </div>\n );\n });\n\n Wrapped.displayName = `WithInteractionTracking(${Component.displayName || Component.name || \"Component\"})`;\n return Wrapped;\n}\n","/*\n * Performance & Web Vitals tracking\n * ---------------------------------\n * A lightweight collector that wires up:\n * - Core Web Vitals (LCP, INP, CLS, FCP, TTFB) via optional dynamic import of `web-vitals`\n * - Long tasks via PerformanceObserver('longtask')\n * - Navigation timing (TTFB, DOMContentLoaded, load, etc.)\n * - Paint timings (FP, FCP fallback)\n * - Resource timings (optionally sampled)\n * - Visibility lifecycle (hidden, pagehide)\n * - Network info (downlink, rtt, effectiveType) when available\n * - Memory snapshot (JS heap) when available (Chromium only)\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport type VitalName = \"LCP\" | \"INP\" | \"CLS\" | \"FCP\" | \"TTFB\";\nexport type PerfCategory =\n | \"web-vitals\"\n | \"nav-timing\"\n | \"paint\"\n | \"resource\"\n | \"longtask\"\n | \"visibility\"\n | \"snapshot\";\n\nexport type PerfEvent = {\n category: PerfCategory;\n name: string; // e.g., \"LCP\", \"DOMContentLoaded\", \"resource:script\"\n value?: number; // ms for most; CLS is unitless\n rating?: \"good\" | \"needs-improvement\" | \"poor\";\n delta?: number; // for web-vitals deltas\n id?: string; // web-vitals id\n url?: string; // current page\n navigationType?: string; // navigate, reload, back_forward, prerender\n ts?: number; // epoch ms when measured\n details?: Record<string, any>;\n};\n\nexport type PerfTracker = (event: PerfEvent) => void;\n\nexport type InitOptions = {\n track: PerfTracker;\n /** Sample a subset of resource entries to avoid high-volume beacons */\n resourceSampleRate?: number; // 0..1, default 0.25\n /** Predicate to include a resource entry */\n resourceFilter?: (e: PerformanceResourceTiming) => boolean;\n /** Include network information (effectiveType/downlink/rtt) snapshots */\n includeNetworkInfo?: boolean;\n /** Include JS heap memory snapshot where supported */\n includeMemorySnapshot?: boolean;\n};\n\nconst now = () => Date.now();\nconst pageURL = () => (typeof location !== \"undefined\" ? location.href : undefined);\nconst navType = () => {\n try {\n const nav = performance.getEntriesByType(\"navigation\")[0] as PerformanceNavigationTiming | undefined;\n return nav?.type ?? (performance as any).navigation?.type;\n } catch {\n return undefined;\n }\n};\n\n// ---------------------------------------------------------------------------\n// Web Vitals (via optional dynamic import of `web-vitals`)\n// ---------------------------------------------------------------------------\nasync function wireWebVitals(track: PerfTracker) {\n try {\n // @ts-ignore - optional dependency; we ignore TS resolution and rely on runtime try/catch\n const mod: any = await import(/* webpackChunkName: \"web-vitals\" */ \"web-vitals\");\n const handlers: [string, (onReport: any, opts?: any) => void, any?][] = [\n [\"LCP\", mod.onLCP],\n [\"INP\", mod.onINP],\n [\"CLS\", mod.onCLS],\n [\"FCP\", mod.onFCP],\n [\"TTFB\", mod.onTTFB],\n ];\n\n handlers.forEach(([name, fn]) => {\n if (typeof fn === \"function\") {\n fn((metric: any) => {\n const { value, rating, delta, id, navigationType: nt, attribution } = metric;\n track({\n category: \"web-vitals\",\n name,\n value,\n rating,\n delta,\n id,\n url: pageURL(),\n navigationType: nt ?? navType(),\n ts: now(),\n details: attribution ? { attribution } : undefined,\n });\n }, { reportAllChanges: true });\n }\n });\n } catch {\n // `web-vitals` not available — silently skip.\n }\n}\n\n// ---------------------------------------------------------------------------\n// Navigation timing & paint timings\n// ---------------------------------------------------------------------------\nfunction reportNavigationTiming(track: PerfTracker) {\n try {\n const nav = performance.getEntriesByType(\"navigation\")[0] as PerformanceNavigationTiming | undefined;\n if (!nav) return;\n const base = {\n category: \"nav-timing\" as const,\n url: pageURL(),\n navigationType: nav.type,\n ts: now(),\n };\n const push = (name: string, value: number | undefined, details?: any) => {\n if (value == null || Number.isNaN(value)) return;\n track({ ...base, name, value, details });\n };\n\n // Common derived timings (ms)\n push(\"TTFB\", nav.responseStart);\n push(\"DNS\", nav.domainLookupEnd - nav.domainLookupStart);\n push(\"TCP\", nav.connectEnd - nav.connectStart);\n push(\"TLS\", nav.secureConnectionStart > 0 ? nav.connectEnd - nav.secureConnectionStart : 0);\n push(\"Request\", nav.responseStart - nav.requestStart);\n push(\"Response\", nav.responseEnd - nav.responseStart);\n push(\"DOMInteractive\", nav.domInteractive);\n push(\"DOMComplete\", nav.domComplete);\n push(\"DOMContentLoaded\", nav.domContentLoadedEventEnd);\n push(\"LoadEvent\", nav.loadEventEnd - nav.loadEventStart);\n push(\"FirstByteToInteractive\", nav.domInteractive - nav.responseStart);\n } catch {\n // ignore\n }\n}\n\nfunction reportPaintTimings(track: PerfTracker) {\n try {\n const paints = performance.getEntriesByType(\"paint\") as PerformanceEntry[];\n const base = { category: \"paint\" as const, url: pageURL(), navigationType: navType(), ts: now() };\n for (const p of paints) {\n track({ ...base, name: p.name, value: p.startTime });\n }\n } catch {\n // ignore\n }\n}\n\n// ---------------------------------------------------------------------------\n// Long tasks\n// ---------------------------------------------------------------------------\nfunction observeLongTasks(track: PerfTracker): () => void {\n if (!(\"PerformanceObserver\" in window)) return () => {};\n let obs: PerformanceObserver | undefined;\n try {\n obs = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n const anyEntry: any = entry as any;\n track({\n category: \"longtask\",\n name: \"longtask\",\n value: entry.duration,\n ts: now(),\n url: pageURL(),\n details: {\n startTime: entry.startTime,\n duration: entry.duration,\n attribution: anyEntry.attribution ?? anyEntry.attribution ?? undefined,\n },\n });\n }\n });\n obs.observe({ type: \"longtask\", buffered: true as any });\n } catch {\n // ignore\n }\n return () => {\n try { obs?.disconnect(); } catch {}\n };\n}\n\n// ---------------------------------------------------------------------------\n// Resource timings (sampled)\n// ---------------------------------------------------------------------------\nfunction observeResources(track: PerfTracker, sampleRate: number, filter?: (e: PerformanceResourceTiming) => boolean): () => void {\n if (!(\"PerformanceObserver\" in window)) return () => {};\n const shouldSample = (Math.max(0, Math.min(1, sampleRate)) || 0.25);\n const take = () => Math.random() < shouldSample;\n let obs: PerformanceObserver | undefined;\n\n const toEvent = (e: PerformanceResourceTiming): PerfEvent => ({\n category: \"resource\",\n name: `resource:${e.initiatorType || \"other\"}`,\n url: e.name,\n ts: now(),\n value: e.duration,\n details: {\n startTime: e.startTime,\n transferSize: (e as any).transferSize,\n encodedBodySize: (e as any).encodedBodySize,\n decodedBodySize: (e as any).decodedBodySize,\n nextHopProtocol: (e as any).nextHopProtocol,\n initiatorType: e.initiatorType,\n },\n });\n\n try {\n obs = new PerformanceObserver((list) => {\n for (const entry of list.getEntries() as PerformanceResourceTiming[]) {\n if (filter && !filter(entry)) continue;\n if (!take()) continue;\n track(toEvent(entry));\n }\n });\n obs.observe({ type: \"resource\", buffered: true as any });\n } catch {\n // ignore\n }\n return () => { try { obs?.disconnect(); } catch {} };\n}\n\n// ---------------------------------------------------------------------------\n// Visibility & lifecycle\n// ---------------------------------------------------------------------------\nfunction wireVisibility(track: PerfTracker): () => void {\n const onHidden = (type: string) => () => {\n track({ category: \"visibility\", name: type, ts: now(), url: pageURL() });\n };\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") onHidden(\"hidden\")();\n });\n window.addEventListener(\"pagehide\", onHidden(\"pagehide\"));\n return () => {\n document.removeEventListener(\"visibilitychange\", onHidden(\"hidden\"));\n window.removeEventListener(\"pagehide\", onHidden(\"pagehide\"));\n };\n}\n\n// ---------------------------------------------------------------------------\n// Snapshots: Network / Memory\n// ---------------------------------------------------------------------------\nfunction snapshotNetwork(track: PerfTracker) {\n const nav: any = (navigator as any);\n const c = nav?.connection || nav?.mozConnection || nav?.webkitConnection;\n if (!c) return;\n track({\n category: \"snapshot\",\n name: \"network-info\",\n ts: now(),\n url: pageURL(),\n details: {\n effectiveType: c.effectiveType,\n downlink: c.downlink,\n rtt: c.rtt,\n saveData: c.saveData,\n },\n });\n}\n\nfunction snapshotMemory(track: PerfTracker) {\n const mem: any = (performance as any).memory;\n if (!mem) return;\n track({\n category: \"snapshot\",\n name: \"js-heap\",\n ts: now(),\n url: pageURL(),\n details: {\n jsHeapSizeLimit: mem.jsHeapSizeLimit,\n totalJSHeapSize: mem.totalJSHeapSize,\n usedJSHeapSize: mem.usedJSHeapSize,\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\nexport function initPerformanceTracking(opts: InitOptions): () => void {\n const {\n track,\n resourceSampleRate = 0.25,\n resourceFilter,\n includeNetworkInfo = true,\n includeMemorySnapshot = false,\n } = opts;\n\n // Web Vitals (async)\n void wireWebVitals(track);\n\n // Navigation & paints (buffered entries are available after DOM ready)\n if (document.readyState === \"complete\") {\n reportNavigationTiming(track);\n reportPaintTimings(track);\n } else {\n window.addEventListener(\"load\", () => {\n reportNavigationTiming(track);\n reportPaintTimings(track);\n }, { once: true });\n }\n\n // Observers\n const offLong = observeLongTasks(track);\n const offRes = observeResources(track, resourceSampleRate, resourceFilter);\n const offVis = wireVisibility(track);\n\n // Snapshots\n if (includeNetworkInfo) snapshotNetwork(track);\n if (includeMemorySnapshot) snapshotMemory(track);\n\n // Teardown\n return () => {\n offLong();\n offRes();\n offVis();\n };\n}\n\nexport default {\n initPerformanceTracking,\n};\n"],"mappings":"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","names":["Wrapped","track"]}
|
|
1
|
+
{"version":3,"sources":["../src/telemetry/analytics.ts","../src/telemetry/withInteractionTracking.tsx","../src/performance/webVitals.ts"],"sourcesContent":["import { PerfEvent } from \"../performance/index.js\";\n\nexport type AnalyticsEvent = {\n name: string;\n props?: Record<string, unknown>;\n ts?: number;\n};\n\nexport interface AnalyticsSink {\n track: (event: AnalyticsEvent) => void;\n page?: (name: string, props?: Record<string, unknown>) => void;\n}\n\n/**\n * Send a performance event through the active analytics sink.\n * We flatten the payload so `dataLayer`-style sinks can consume it directly.\n */\nexport const trackPerf = (e: PerfEvent) => {\n // Preserve provided timestamp if present; otherwise add one in the sink (or here)\n const { ts, ...rest } = e;\n activeSink.track({\n name: \"perf\",\n props: rest,\n ts: ts ?? Date.now(),\n });\n};\n\nconst isProd =\n (typeof import.meta !== \"undefined\" &&\n (import.meta as any).env?.MODE === \"production\") ||\n process.env.NODE_ENV === \"production\";\n\n// Example sinks\nconst consoleSink: AnalyticsSink = {\n track: (event) => console.info(event.name, event.props),\n page: (name, props) => console.info(name, props),\n};\n\n// Replace with your real one later:\nconst dataLayerSink: AnalyticsSink = {\n track: (event) => {\n // Example: Google Tag Manager dataLayer\n (window as any).dataLayer = (window as any).dataLayer || [];\n (window as any).dataLayer.push({\n event: event.name,\n ...event.props,\n ts: event.ts ?? Date.now(),\n });\n },\n page: (name, props) => {\n (window as any).dataLayer = (window as any).dataLayer || [];\n (window as any).dataLayer.push({\n event: \"page_view\",\n page: name,\n ...props,\n });\n },\n};\n\nlet activeSink: AnalyticsSink = isProd ? dataLayerSink : consoleSink;\n\nexport const setAnalyticsSink = (sink: AnalyticsSink) => {\n activeSink = sink;\n};\n\nexport const track = (name: string, props?: Record<string, unknown>) =>\n activeSink.track({ name, props, ts: Date.now() });\n\nexport const page = (name: string, props?: Record<string, unknown>) =>\n activeSink.page?.(name, props);\n","import React from \"react\";\nimport { track } from \"./analytics.js\";\n\ntype WithInteractionOpts = {\n origin?: string; // e.g. story name\n};\n\nexport function withInteractionTracking<P extends object, R = unknown>(\n Component: React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n >,\n opts?: WithInteractionOpts\n): React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n>;\n\nexport function withInteractionTracking<P extends object>(\n Component: React.ComponentType<P>,\n opts?: WithInteractionOpts\n): React.FC<P>;\n\nexport function withInteractionTracking<P extends object, R = unknown>(\n Component:\n | React.ForwardRefExoticComponent<\n React.PropsWithoutRef<P> & React.RefAttributes<R>\n >\n | React.ComponentType<P>,\n { origin }: WithInteractionOpts = {}\n): any {\n const Wrapped = React.forwardRef<R, P>(function Wrapped(props, ref) {\n const onInteractionCapture = (\n e: React.PointerEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>\n ) => {\n // Normalize event type\n const isKey = (e as React.KeyboardEvent).key !== undefined;\n const isPointer = (e as React.PointerEvent).pointerType !== undefined;\n const type = isKey ? \"keydown\" : isPointer ? \"pointerup\" : e.type;\n\n // Only track meaningful keyboard activations (Enter/Space)\n let key: string | undefined;\n if (isKey) {\n key = (e as React.KeyboardEvent).key;\n if (key !== \"Enter\" && key !== \" \") return; // ignore other keys\n }\n\n // For pointers: only primary button releases\n if (isPointer) {\n const pe = e as React.PointerEvent;\n if (type === \"pointerup\" && pe.button !== 0) return;\n }\n\n const target = e.target as HTMLElement | null;\n const label =\n target?.getAttribute?.(\"aria-label\") ||\n target?.getAttribute?.(\"data-analytics-label\") ||\n target?.textContent?.trim() ||\n target?.tagName;\n\n track(\"ui.interaction\", {\n origin,\n label,\n type,\n key,\n pointerType: isPointer ? (e as React.PointerEvent).pointerType : undefined,\n });\n };\n\n const Comp = Component as any; // Allow ref passing for both ref-aware and plain components\n return (\n <div\n onPointerUpCapture={onInteractionCapture}\n onKeyDownCapture={onInteractionCapture}\n >\n <Comp ref={ref} {...(props as any)} />\n </div>\n );\n });\n\n Wrapped.displayName = `WithInteractionTracking(${Component.displayName || Component.name || \"Component\"})`;\n return Wrapped;\n}\n","/*\n * Performance & Web Vitals tracking\n * ---------------------------------\n * A lightweight collector that wires up:\n * - Core Web Vitals (LCP, INP, CLS, FCP, TTFB) via optional dynamic import of `web-vitals`\n * - Long tasks via PerformanceObserver('longtask')\n * - Navigation timing (TTFB, DOMContentLoaded, load, etc.)\n * - Paint timings (FP, FCP fallback)\n * - Resource timings (optionally sampled)\n * - Visibility lifecycle (hidden, pagehide)\n * - Network info (downlink, rtt, effectiveType) when available\n * - Memory snapshot (JS heap) when available (Chromium only)\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport type VitalName = \"LCP\" | \"INP\" | \"CLS\" | \"FCP\" | \"TTFB\";\nexport type PerfCategory =\n | \"web-vitals\"\n | \"nav-timing\"\n | \"paint\"\n | \"resource\"\n | \"longtask\"\n | \"visibility\"\n | \"snapshot\";\n\nexport type PerfEvent = {\n category: PerfCategory;\n name: string; // e.g., \"LCP\", \"DOMContentLoaded\", \"resource:script\"\n value?: number; // ms for most; CLS is unitless\n rating?: \"good\" | \"needs-improvement\" | \"poor\";\n delta?: number; // for web-vitals deltas\n id?: string; // web-vitals id\n url?: string; // current page\n navigationType?: string; // navigate, reload, back_forward, prerender\n ts?: number; // epoch ms when measured\n details?: Record<string, any>;\n};\n\nexport type PerfTracker = (event: PerfEvent) => void;\n\nexport type InitOptions = {\n track: PerfTracker;\n /** Sample a subset of resource entries to avoid high-volume beacons */\n resourceSampleRate?: number; // 0..1, default 0.25\n /** Predicate to include a resource entry */\n resourceFilter?: (e: PerformanceResourceTiming) => boolean;\n /** Include network information (effectiveType/downlink/rtt) snapshots */\n includeNetworkInfo?: boolean;\n /** Include JS heap memory snapshot where supported */\n includeMemorySnapshot?: boolean;\n};\n\nconst now = () => Date.now();\nconst pageURL = () => (typeof location !== \"undefined\" ? location.href : undefined);\nconst navType = () => {\n try {\n const nav = performance.getEntriesByType(\"navigation\")[0] as PerformanceNavigationTiming | undefined;\n return nav?.type ?? (performance as any).navigation?.type;\n } catch {\n return undefined;\n }\n};\n\n// ---------------------------------------------------------------------------\n// Web Vitals (via optional dynamic import of `web-vitals`)\n// ---------------------------------------------------------------------------\nasync function wireWebVitals(track: PerfTracker) {\n try {\n // @ts-ignore - optional dependency; we ignore TS resolution and rely on runtime try/catch\n const mod: any = await import(/* webpackChunkName: \"web-vitals\" */ \"web-vitals\");\n const handlers: [string, (onReport: any, opts?: any) => void, any?][] = [\n [\"LCP\", mod.onLCP],\n [\"INP\", mod.onINP],\n [\"CLS\", mod.onCLS],\n [\"FCP\", mod.onFCP],\n [\"TTFB\", mod.onTTFB],\n ];\n\n handlers.forEach(([name, fn]) => {\n if (typeof fn === \"function\") {\n fn((metric: any) => {\n const { value, rating, delta, id, navigationType: nt, attribution } = metric;\n track({\n category: \"web-vitals\",\n name,\n value,\n rating,\n delta,\n id,\n url: pageURL(),\n navigationType: nt ?? navType(),\n ts: now(),\n details: attribution ? { attribution } : undefined,\n });\n }, { reportAllChanges: true });\n }\n });\n } catch {\n // `web-vitals` not available — silently skip.\n }\n}\n\n// ---------------------------------------------------------------------------\n// Navigation timing & paint timings\n// ---------------------------------------------------------------------------\nfunction reportNavigationTiming(track: PerfTracker) {\n try {\n const nav = performance.getEntriesByType(\"navigation\")[0] as PerformanceNavigationTiming | undefined;\n if (!nav) return;\n const base = {\n category: \"nav-timing\" as const,\n url: pageURL(),\n navigationType: nav.type,\n ts: now(),\n };\n const push = (name: string, value: number | undefined, details?: any) => {\n if (value == null || Number.isNaN(value)) return;\n track({ ...base, name, value, details });\n };\n\n // Common derived timings (ms)\n push(\"TTFB\", nav.responseStart);\n push(\"DNS\", nav.domainLookupEnd - nav.domainLookupStart);\n push(\"TCP\", nav.connectEnd - nav.connectStart);\n push(\"TLS\", nav.secureConnectionStart > 0 ? nav.connectEnd - nav.secureConnectionStart : 0);\n push(\"Request\", nav.responseStart - nav.requestStart);\n push(\"Response\", nav.responseEnd - nav.responseStart);\n push(\"DOMInteractive\", nav.domInteractive);\n push(\"DOMComplete\", nav.domComplete);\n push(\"DOMContentLoaded\", nav.domContentLoadedEventEnd);\n push(\"LoadEvent\", nav.loadEventEnd - nav.loadEventStart);\n push(\"FirstByteToInteractive\", nav.domInteractive - nav.responseStart);\n } catch {\n // ignore\n }\n}\n\nfunction reportPaintTimings(track: PerfTracker) {\n try {\n const paints = performance.getEntriesByType(\"paint\") as PerformanceEntry[];\n const base = { category: \"paint\" as const, url: pageURL(), navigationType: navType(), ts: now() };\n for (const p of paints) {\n track({ ...base, name: p.name, value: p.startTime });\n }\n } catch {\n // ignore\n }\n}\n\n// ---------------------------------------------------------------------------\n// Long tasks\n// ---------------------------------------------------------------------------\nfunction observeLongTasks(track: PerfTracker): () => void {\n if (!(\"PerformanceObserver\" in window)) return () => {};\n let obs: PerformanceObserver | undefined;\n try {\n obs = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n const anyEntry: any = entry as any;\n track({\n category: \"longtask\",\n name: \"longtask\",\n value: entry.duration,\n ts: now(),\n url: pageURL(),\n details: {\n startTime: entry.startTime,\n duration: entry.duration,\n attribution: anyEntry.attribution ?? anyEntry.attribution ?? undefined,\n },\n });\n }\n });\n obs.observe({ type: \"longtask\", buffered: true as any });\n } catch {\n // ignore\n }\n return () => {\n try { obs?.disconnect(); } catch {}\n };\n}\n\n// ---------------------------------------------------------------------------\n// Resource timings (sampled)\n// ---------------------------------------------------------------------------\nfunction observeResources(track: PerfTracker, sampleRate: number, filter?: (e: PerformanceResourceTiming) => boolean): () => void {\n if (!(\"PerformanceObserver\" in window)) return () => {};\n const shouldSample = (Math.max(0, Math.min(1, sampleRate)) || 0.25);\n const take = () => Math.random() < shouldSample;\n let obs: PerformanceObserver | undefined;\n\n const toEvent = (e: PerformanceResourceTiming): PerfEvent => ({\n category: \"resource\",\n name: `resource:${e.initiatorType || \"other\"}`,\n url: e.name,\n ts: now(),\n value: e.duration,\n details: {\n startTime: e.startTime,\n transferSize: (e as any).transferSize,\n encodedBodySize: (e as any).encodedBodySize,\n decodedBodySize: (e as any).decodedBodySize,\n nextHopProtocol: (e as any).nextHopProtocol,\n initiatorType: e.initiatorType,\n },\n });\n\n try {\n obs = new PerformanceObserver((list) => {\n for (const entry of list.getEntries() as PerformanceResourceTiming[]) {\n if (filter && !filter(entry)) continue;\n if (!take()) continue;\n track(toEvent(entry));\n }\n });\n obs.observe({ type: \"resource\", buffered: true as any });\n } catch {\n // ignore\n }\n return () => { try { obs?.disconnect(); } catch {} };\n}\n\n// ---------------------------------------------------------------------------\n// Visibility & lifecycle\n// ---------------------------------------------------------------------------\nfunction wireVisibility(track: PerfTracker): () => void {\n const onHidden = (type: string) => () => {\n track({ category: \"visibility\", name: type, ts: now(), url: pageURL() });\n };\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") onHidden(\"hidden\")();\n });\n window.addEventListener(\"pagehide\", onHidden(\"pagehide\"));\n return () => {\n document.removeEventListener(\"visibilitychange\", onHidden(\"hidden\"));\n window.removeEventListener(\"pagehide\", onHidden(\"pagehide\"));\n };\n}\n\n// ---------------------------------------------------------------------------\n// Snapshots: Network / Memory\n// ---------------------------------------------------------------------------\nfunction snapshotNetwork(track: PerfTracker) {\n const nav: any = (navigator as any);\n const c = nav?.connection || nav?.mozConnection || nav?.webkitConnection;\n if (!c) return;\n track({\n category: \"snapshot\",\n name: \"network-info\",\n ts: now(),\n url: pageURL(),\n details: {\n effectiveType: c.effectiveType,\n downlink: c.downlink,\n rtt: c.rtt,\n saveData: c.saveData,\n },\n });\n}\n\nfunction snapshotMemory(track: PerfTracker) {\n const mem: any = (performance as any).memory;\n if (!mem) return;\n track({\n category: \"snapshot\",\n name: \"js-heap\",\n ts: now(),\n url: pageURL(),\n details: {\n jsHeapSizeLimit: mem.jsHeapSizeLimit,\n totalJSHeapSize: mem.totalJSHeapSize,\n usedJSHeapSize: mem.usedJSHeapSize,\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\nexport function initPerformanceTracking(opts: InitOptions): () => void {\n const {\n track,\n resourceSampleRate = 0.25,\n resourceFilter,\n includeNetworkInfo = true,\n includeMemorySnapshot = false,\n } = opts;\n\n // Web Vitals (async)\n void wireWebVitals(track);\n\n // Navigation & paints (buffered entries are available after DOM ready)\n if (document.readyState === \"complete\") {\n reportNavigationTiming(track);\n reportPaintTimings(track);\n } else {\n window.addEventListener(\"load\", () => {\n reportNavigationTiming(track);\n reportPaintTimings(track);\n }, { once: true });\n }\n\n // Observers\n const offLong = observeLongTasks(track);\n const offRes = observeResources(track, resourceSampleRate, resourceFilter);\n const offVis = wireVisibility(track);\n\n // Snapshots\n if (includeNetworkInfo) snapshotNetwork(track);\n if (includeMemorySnapshot) snapshotMemory(track);\n\n // Teardown\n return () => {\n offLong();\n offRes();\n offVis();\n };\n}\n\nexport default {\n initPerformanceTracking,\n};\n"],"mappings":"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","names":["Wrapped","track"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plasius/nfr",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "a small, typed Non-Functional-Requirement (NFR) framework that cleanly adapts to different cloud vendors (GA4, PostHog, Azure Application Insights, etc.) and different concerns (analytics/events, metrics, tracing, logs, feature flags, performance).",
|
|
5
5
|
"homepage": "https://github.com/Plasius-LTD/nfr#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"types": "./dist/index.d.ts",
|
|
24
24
|
"import": "./dist/index.js",
|
|
25
25
|
"require": "./dist/index.cjs"
|
|
26
|
-
}
|
|
26
|
+
},
|
|
27
|
+
"./package.json": "./package.json"
|
|
27
28
|
},
|
|
28
29
|
"scripts": {
|
|
29
30
|
"build": "tsup",
|
|
@@ -61,5 +62,18 @@
|
|
|
61
62
|
"web-vitals": {
|
|
62
63
|
"optional": true
|
|
63
64
|
}
|
|
64
|
-
}
|
|
65
|
+
},
|
|
66
|
+
"publishConfig": {
|
|
67
|
+
"access": "public"
|
|
68
|
+
},
|
|
69
|
+
"funding": [
|
|
70
|
+
{
|
|
71
|
+
"type": "patreon",
|
|
72
|
+
"url": "https://patreon.com/PlasiusLTD"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"type": "github",
|
|
76
|
+
"url": "https://github.com/sponsors/Plasius-LTD"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
65
79
|
}
|