{"_id":"@affectively/aeon-flux-analytics","_rev":"2-b60d7f91af045001587768abce082602","name":"@affectively/aeon-flux-analytics","dist-tags":{"latest":"5.0.0"},"versions":{"5.0.0":{"name":"@affectively/aeon-flux-analytics","version":"5.0.0","keywords":["aeon","pages","analytics","gtm","google-tag-manager","click-tracking","merkle-tree"],"license":"UNLICENSED","_id":"@affectively/aeon-flux-analytics@5.0.0","maintainers":[{"name":"buley","email":"buley@outlook.com"}],"homepage":"https://github.com/affectively/aeon-flux#readme","bugs":{"url":"https://github.com/affectively/aeon-flux/issues"},"dist":{"shasum":"fcbbee3bc41b8cea8493fd8b366f14cf2d352afe","tarball":"https://registry.npmjs.org/@affectively/aeon-flux-analytics/-/aeon-flux-analytics-5.0.0.tgz","fileCount":15,"integrity":"sha512-Pin2lM7vbo35ZJfS0sn0NcqNdnyycK2iY6OUzLnoJ07PJUYv0d82317aoUmzZzXd5qswI6flK5ZURo/h5iOsBQ==","signatures":[{"sig":"MEUCIHC08QQxDO+C4hz/bbTZ+Nk0a+icLGtOPpZK/nR9jLJYAiEAv0FqtEtnU4xJEtULkUlvOYqKPQCWgqpI042hlri/7L8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":257374},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./react":{"types":"./dist/provider.d.ts","import":"./dist/provider.js"}},"scripts":{"dev":"bun --watch ./src/index.ts","test":"bun test","build":"bun run build.ts && tsc --declaration --emitDeclarationOnly","prepublishOnly":"bun run build"},"_npmUser":{"name":"buley","email":"buley@outlook.com"},"repository":{"url":"git+https://github.com/affectively/aeon-flux.git","type":"git"},"_npmVersion":"11.8.0","description":"Automatic click tracking and GTM integration for @affectively/aeon-flux","directories":{},"_nodeVersion":"25.5.0","_hasShrinkwrap":false,"devDependencies":{"react":"^19.0.0","typescript":"^5.7.0","@types/react":"^19.0.0"},"peerDependencies":{"react":">=18.0.0"},"_npmOperationalInternal":{"tmp":"tmp/aeon-flux-analytics_5.0.0_1773156001997_0.33142652690066243","host":"s3://npm-registry-packages-npm-production"},"deprecated":"This package is deprecated and no longer supported. Do not use."}},"time":{"created":"2026-03-10T15:20:01.896Z","modified":"2026-03-27T19:58:27.054Z","5.0.0":"2026-03-10T15:20:02.159Z"},"bugs":{"url":"https://github.com/affectively/aeon-flux/issues"},"license":"UNLICENSED","homepage":"https://github.com/affectively/aeon-flux#readme","keywords":["aeon","pages","analytics","gtm","google-tag-manager","click-tracking","merkle-tree"],"repository":{"url":"git+https://github.com/affectively/aeon-flux.git","type":"git"},"description":"Automatic click tracking and GTM integration for @affectively/aeon-flux","maintainers":[{"name":"buley","email":"buley@outlook.com"}],"readme":"# @affectively/aeon-flux-analytics\n\n**Zero-instrumentation analytics.** Automatic click tracking with Merkle tree node IDs and rich ESI context.\n\n```bash\nbun add @affectively/aeon-flux-analytics\n```\n\n## What is this?\n\nAeon Analytics automatically tracks every click in your application without any manual instrumentation. Each click event includes:\n\n- **Merkle hash** of the clicked component (content-addressable, stable across renders)\n- **Full tree path** from root to clicked node\n- **Rich ESI context** (user tier, emotion state, features, session info)\n- **Element metadata** (text, aria-label, role, href)\n- **Position data** (viewport and document coordinates)\n\nEverything flows to GTM dataLayer for GA4 custom events.\n\n## Quick Start\n\n### 1. Server-Side: Inject GTM\n\n```typescript\n// In your HTML template\nconst gtmContainerId = env.GTM_CONTAINER_ID;\n\nconst html = `\n<!DOCTYPE html>\n<html>\n<head>\n  <!-- GTM Script -->\n  <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':\n  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],\n  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=\n  'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);\n  })(window,document,'script','dataLayer','${gtmContainerId}');</script>\n</head>\n<body>\n  <!-- GTM noscript -->\n  <noscript><iframe src=\"https://www.googletagmanager.com/ns.html?id=${gtmContainerId}\"\n  height=\"0\" width=\"0\" style=\"display:none;visibility:hidden\"></iframe></noscript>\n  ...\n</body>\n</html>\n`;\n```\n\n### 2. Client-Side: Initialize Tracking\n\n```typescript\nimport {\n  initClickTracker,\n  initContextBridgeWithRetry,\n  pushPageView,\n  syncESIToDataLayer,\n} from '@affectively/aeon-flux-analytics';\n\n// Initialize on page load\ninitContextBridgeWithRetry({ maxRetries: 3, retryDelayMs: 500 });\ninitClickTracker({\n  debounceMs: 100,\n  maxTextLength: 150,\n  excludeSelectors: ['.no-track', '[data-no-track]'],\n  includePosition: true,\n});\npushPageView();\n\n// Track SPA navigation\nhistory.pushState = function(...args) {\n  originalPushState(...args);\n  syncESIToDataLayer();\n  pushPageView();\n};\n```\n\n### 3. React Provider (Optional)\n\n```tsx\nimport { AeonAnalyticsProvider } from '@affectively/aeon-flux-analytics';\n\nexport default function App({ Component, pageProps }) {\n  return (\n    <AeonAnalyticsProvider\n      gtmContainerId=\"GTM-XXXXXX\"\n      trackClicks={true}\n      syncESIContext={true}\n    >\n      <Component {...pageProps} />\n    </AeonAnalyticsProvider>\n  );\n}\n```\n\n## Merkle Tree Hashing\n\nEach component gets a deterministic hash based on its content:\n\n```typescript\nimport { hashNodeSync, buildMerkleTreeSync } from '@affectively/aeon-flux-analytics';\n\n// Hash = SHA-256(type + sorted_props + child_hashes)\nconst hash = hashNodeSync('Button', { onClick: fn }, ['child1hash']);\n// Returns: \"a1b2c3d4e5f6\" (12-char truncated hash)\n\n// Build tree from component tree\nconst merkleTree = buildMerkleTreeSync(componentTree);\n// Map<nodeId, MerkleNode> with hashes and paths\n```\n\n### Why Merkle Trees?\n\n- **Stable IDs** - Same content = same hash, even if DOM order changes\n- **Tree paths** - Full ancestry for every click\n- **Change detection** - Compare trees to find what changed\n- **Cacheable** - Same tree = same hashes\n\n## DataLayer Events\n\n### Context Event (on page load)\n```javascript\n{\n  event: 'aeon.context',\n  user: { tier: 'pro', id: 'user_123', sessionId: 'sess_abc' },\n  emotion: { primary: 'focused', valence: 0.3, arousal: 0.6 },\n  features: { aiInference: true, emotionTracking: true },\n  device: { viewport: { width: 1920, height: 1080 }, connection: '4g' },\n  time: { localHour: 14, timezone: 'America/New_York' }\n}\n```\n\n### Click Event\n```javascript\n{\n  event: 'aeon.click',\n  aeon: { version: '0.1.0', timestamp: 1707321600000 },\n  click: {\n    merkleHash: 'a1b2c3d4e5f6',\n    treePath: ['root', 'layout', 'header', 'nav', 'settings-button'],\n    treePathHashes: ['f1e2d3c4b5a6', 'b2c3d4e5f6a1', ...],\n    element: {\n      tagName: 'BUTTON',\n      text: 'Settings',\n      ariaLabel: 'Open settings menu',\n      role: 'button'\n    },\n    position: { x: 1450, y: 32, viewportX: 1450, viewportY: 32 }\n  },\n  context: {\n    userTier: 'pro',\n    emotionState: { primary: 'focused', valence: 0.3, arousal: 0.6 },\n    sessionId: 'sess_abc'\n  }\n}\n```\n\n### Page View Event\n```javascript\n{\n  event: 'aeon.pageview',\n  page: {\n    path: '/dashboard',\n    title: 'Dashboard - AFFECTIVELY',\n    merkleRoot: 'abc123def456'\n  },\n  user: { tier: 'pro', sessionId: 'sess_abc' }\n}\n```\n\n## API Reference\n\n### Click Tracking\n\n```typescript\n// Initialize\ninitClickTracker(options?: ClickTrackingOptions): void\n\n// Stop tracking\nstopClickTracker(): void\n\n// Check status\nisClickTrackerActive(): boolean\n\n// Manual tracking\ntrackClick(element: HTMLElement, event: MouseEvent): void\ntrackInteraction(type: string, data: Record<string, unknown>): void\n```\n\n### Context Bridge\n\n```typescript\n// ESI state access\ngetESIState(): ESIState | null\nhasESIState(): boolean\ngetESIProperty<K extends keyof ESIState>(key: K): ESIState[K] | null\n\n// Sync to dataLayer\nsyncESIToDataLayer(): void\npushPageView(): void\n\n// Watch for changes\nwatchESIChanges(): () => void\ninitContextBridge(): void\ninitContextBridgeWithRetry(options: { maxRetries: number, retryDelayMs: number }): void\n\n// Utilities\nisAdmin(): boolean\nhasFeature(feature: string): boolean\nmeetsTierRequirement(tier: UserTier): boolean\ngetUserTier(): UserTier\ngetEmotionState(): EmotionState | null\n```\n\n### Merkle Tree\n\n```typescript\n// Sync hashing (uses djb2)\nhashNodeSync(type: string, props: object, childHashes: string[]): string\nbuildMerkleTreeSync(tree: ComponentTree): MerkleTree\n\n// Async hashing (uses SHA-256)\nhashNodeAsync(type: string, props: object, childHashes: string[]): Promise<string>\nbuildMerkleTree(tree: ComponentTree): Promise<MerkleTree>\n\n// DOM helpers\ngetMerkleAttributes(node: MerkleNode): Record<string, string>\nparseMerkleFromElement(el: HTMLElement): MerkleNode | null\nfindNearestMerkleElement(el: HTMLElement): HTMLElement | null\n\n// Verification\nverifyMerkleTree(tree: MerkleTree): boolean\ndiffMerkleTrees(a: MerkleTree, b: MerkleTree): MerkleDiff\n```\n\n### GTM Loader\n\n```typescript\n// Inject GTM\ninjectGTM(config: GTMConfig): void\ninjectGTMNoScript(containerId: string): void\ninitializeGTM(config: GTMConfig): void\n\n// SSR helpers\ngenerateGTMScriptTag(containerId: string): string\ngenerateGTMNoScriptTag(containerId: string): string\ngenerateDataLayerScript(initialData: object): string\n\n// Status\nisGTMInjected(): boolean\nisGTMReady(): boolean\nwaitForGTM(timeout?: number): Promise<void>\n```\n\n## Configuration\n\n```typescript\ninterface AnalyticsConfig {\n  // Required: GTM container ID\n  gtmContainerId: string;           // 'GTM-XXXXXX'\n\n  // Optional: Customize behavior\n  trackClicks?: boolean;            // Default: true\n  trackPageViews?: boolean;         // Default: true\n  syncESIContext?: boolean;         // Default: true\n\n  // Click tracking options\n  clickOptions?: {\n    debounceMs?: number;            // Default: 0\n    maxTextLength?: number;         // Default: 100\n    excludeSelectors?: string[];    // e.g., ['.no-track']\n    includePosition?: boolean;      // Default: true\n  };\n\n  // Data layer customization\n  dataLayerName?: string;           // Default: 'dataLayer'\n  eventPrefix?: string;             // Default: 'aeon'\n}\n```\n\n## ESI State Types\n\n```typescript\ntype UserTier = 'free' | 'starter' | 'pro' | 'enterprise' | 'admin';\n\ninterface ESIState {\n  userTier: UserTier;\n  isAdmin?: boolean;\n  userId?: string;\n  sessionId?: string;\n  isNewSession?: boolean;\n  emotionState?: {\n    primary: string;\n    valence: number;\n    arousal: number;\n  };\n  preferences?: Record<string, unknown>;\n  features?: Record<string, boolean>;\n  recentPages?: string[];\n  viewport?: { width: number; height: number };\n  connection?: string;\n  localHour?: number;\n  timezone?: string;\n}\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}