@affectively/aeon-pages 1.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/README.md +625 -0
  3. package/examples/basic/aeon.config.ts +39 -0
  4. package/examples/basic/components/Cursor.tsx +86 -0
  5. package/examples/basic/components/OfflineIndicator.tsx +103 -0
  6. package/examples/basic/components/PresenceBar.tsx +77 -0
  7. package/examples/basic/package.json +20 -0
  8. package/examples/basic/pages/index.tsx +80 -0
  9. package/package.json +101 -0
  10. package/packages/analytics/README.md +309 -0
  11. package/packages/analytics/build.ts +35 -0
  12. package/packages/analytics/package.json +50 -0
  13. package/packages/analytics/src/click-tracker.ts +368 -0
  14. package/packages/analytics/src/context-bridge.ts +319 -0
  15. package/packages/analytics/src/data-layer.ts +302 -0
  16. package/packages/analytics/src/gtm-loader.ts +239 -0
  17. package/packages/analytics/src/index.ts +230 -0
  18. package/packages/analytics/src/merkle-tree.ts +489 -0
  19. package/packages/analytics/src/provider.tsx +300 -0
  20. package/packages/analytics/src/types.ts +320 -0
  21. package/packages/analytics/src/use-analytics.ts +296 -0
  22. package/packages/analytics/tsconfig.json +19 -0
  23. package/packages/benchmarks/src/benchmark.test.ts +691 -0
  24. package/packages/cli/dist/index.js +61899 -0
  25. package/packages/cli/package.json +43 -0
  26. package/packages/cli/src/commands/build.test.ts +682 -0
  27. package/packages/cli/src/commands/build.ts +890 -0
  28. package/packages/cli/src/commands/dev.ts +473 -0
  29. package/packages/cli/src/commands/init.ts +409 -0
  30. package/packages/cli/src/commands/start.ts +297 -0
  31. package/packages/cli/src/index.ts +105 -0
  32. package/packages/directives/src/use-aeon.ts +272 -0
  33. package/packages/mcp-server/package.json +51 -0
  34. package/packages/mcp-server/src/index.ts +178 -0
  35. package/packages/mcp-server/src/resources.ts +346 -0
  36. package/packages/mcp-server/src/tools/index.ts +36 -0
  37. package/packages/mcp-server/src/tools/navigation.ts +545 -0
  38. package/packages/mcp-server/tsconfig.json +21 -0
  39. package/packages/react/package.json +40 -0
  40. package/packages/react/src/Link.tsx +388 -0
  41. package/packages/react/src/components/InstallPrompt.tsx +286 -0
  42. package/packages/react/src/components/OfflineDiagnostics.tsx +677 -0
  43. package/packages/react/src/components/PushNotifications.tsx +453 -0
  44. package/packages/react/src/hooks/useAeonNavigation.ts +219 -0
  45. package/packages/react/src/hooks/useConflicts.ts +277 -0
  46. package/packages/react/src/hooks/useNetworkState.ts +209 -0
  47. package/packages/react/src/hooks/usePilotNavigation.ts +254 -0
  48. package/packages/react/src/hooks/useServiceWorker.ts +278 -0
  49. package/packages/react/src/hooks.ts +195 -0
  50. package/packages/react/src/index.ts +151 -0
  51. package/packages/react/src/provider.tsx +467 -0
  52. package/packages/react/tsconfig.json +19 -0
  53. package/packages/runtime/README.md +399 -0
  54. package/packages/runtime/build.ts +48 -0
  55. package/packages/runtime/package.json +71 -0
  56. package/packages/runtime/schema.sql +40 -0
  57. package/packages/runtime/src/api-routes.ts +465 -0
  58. package/packages/runtime/src/benchmark.ts +171 -0
  59. package/packages/runtime/src/cache.ts +479 -0
  60. package/packages/runtime/src/durable-object.ts +1341 -0
  61. package/packages/runtime/src/index.ts +360 -0
  62. package/packages/runtime/src/navigation.test.ts +421 -0
  63. package/packages/runtime/src/navigation.ts +422 -0
  64. package/packages/runtime/src/nextjs-adapter.ts +272 -0
  65. package/packages/runtime/src/offline/encrypted-queue.test.ts +607 -0
  66. package/packages/runtime/src/offline/encrypted-queue.ts +478 -0
  67. package/packages/runtime/src/offline/encryption.test.ts +412 -0
  68. package/packages/runtime/src/offline/encryption.ts +397 -0
  69. package/packages/runtime/src/offline/types.ts +465 -0
  70. package/packages/runtime/src/predictor.ts +371 -0
  71. package/packages/runtime/src/registry.ts +351 -0
  72. package/packages/runtime/src/router/context-extractor.ts +661 -0
  73. package/packages/runtime/src/router/esi-control-react.tsx +2053 -0
  74. package/packages/runtime/src/router/esi-control.ts +541 -0
  75. package/packages/runtime/src/router/esi-cyrano.ts +779 -0
  76. package/packages/runtime/src/router/esi-format-react.tsx +1744 -0
  77. package/packages/runtime/src/router/esi-react.tsx +1065 -0
  78. package/packages/runtime/src/router/esi-translate-observer.ts +476 -0
  79. package/packages/runtime/src/router/esi-translate-react.tsx +556 -0
  80. package/packages/runtime/src/router/esi-translate.ts +503 -0
  81. package/packages/runtime/src/router/esi.ts +666 -0
  82. package/packages/runtime/src/router/heuristic-adapter.test.ts +295 -0
  83. package/packages/runtime/src/router/heuristic-adapter.ts +557 -0
  84. package/packages/runtime/src/router/index.ts +298 -0
  85. package/packages/runtime/src/router/merkle-capability.ts +473 -0
  86. package/packages/runtime/src/router/speculation.ts +451 -0
  87. package/packages/runtime/src/router/types.ts +630 -0
  88. package/packages/runtime/src/router.test.ts +470 -0
  89. package/packages/runtime/src/router.ts +302 -0
  90. package/packages/runtime/src/server.ts +481 -0
  91. package/packages/runtime/src/service-worker-push.ts +319 -0
  92. package/packages/runtime/src/service-worker.ts +553 -0
  93. package/packages/runtime/src/skeleton-hydrate.ts +237 -0
  94. package/packages/runtime/src/speculation.test.ts +389 -0
  95. package/packages/runtime/src/speculation.ts +486 -0
  96. package/packages/runtime/src/storage.test.ts +1297 -0
  97. package/packages/runtime/src/storage.ts +1048 -0
  98. package/packages/runtime/src/sync/conflict-resolver.test.ts +528 -0
  99. package/packages/runtime/src/sync/conflict-resolver.ts +565 -0
  100. package/packages/runtime/src/sync/coordinator.test.ts +608 -0
  101. package/packages/runtime/src/sync/coordinator.ts +596 -0
  102. package/packages/runtime/src/tree-compiler.ts +295 -0
  103. package/packages/runtime/src/types.ts +728 -0
  104. package/packages/runtime/src/worker.ts +327 -0
  105. package/packages/runtime/tsconfig.json +20 -0
  106. package/packages/runtime/wasm/aeon_pages_runtime.d.ts +504 -0
  107. package/packages/runtime/wasm/aeon_pages_runtime.js +1657 -0
  108. package/packages/runtime/wasm/aeon_pages_runtime_bg.wasm +0 -0
  109. package/packages/runtime/wasm/aeon_pages_runtime_bg.wasm.d.ts +196 -0
  110. package/packages/runtime/wasm/package.json +21 -0
  111. package/packages/runtime/wrangler.toml +41 -0
  112. package/packages/runtime-wasm/Cargo.lock +436 -0
  113. package/packages/runtime-wasm/Cargo.toml +29 -0
  114. package/packages/runtime-wasm/pkg/aeon_pages_runtime.d.ts +480 -0
  115. package/packages/runtime-wasm/pkg/aeon_pages_runtime.js +1568 -0
  116. package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm +0 -0
  117. package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm.d.ts +192 -0
  118. package/packages/runtime-wasm/pkg/package.json +21 -0
  119. package/packages/runtime-wasm/src/hydrate.rs +352 -0
  120. package/packages/runtime-wasm/src/lib.rs +191 -0
  121. package/packages/runtime-wasm/src/render.rs +629 -0
  122. package/packages/runtime-wasm/src/router.rs +298 -0
  123. package/packages/runtime-wasm/src/skeleton.rs +430 -0
  124. package/rfcs/RFC-001-ZERO-DEPENDENCY-RENDERING.md +1446 -0
@@ -0,0 +1,309 @@
1
+ # @affectively/aeon-pages-analytics
2
+
3
+ **Zero-instrumentation analytics.** Automatic click tracking with Merkle tree node IDs and rich ESI context.
4
+
5
+ ```bash
6
+ bun add @affectively/aeon-pages-analytics
7
+ ```
8
+
9
+ ## What is this?
10
+
11
+ Aeon Analytics automatically tracks every click in your application without any manual instrumentation. Each click event includes:
12
+
13
+ - **Merkle hash** of the clicked component (content-addressable, stable across renders)
14
+ - **Full tree path** from root to clicked node
15
+ - **Rich ESI context** (user tier, emotion state, features, session info)
16
+ - **Element metadata** (text, aria-label, role, href)
17
+ - **Position data** (viewport and document coordinates)
18
+
19
+ Everything flows to GTM dataLayer for GA4 custom events.
20
+
21
+ ## Quick Start
22
+
23
+ ### 1. Server-Side: Inject GTM
24
+
25
+ ```typescript
26
+ // In your HTML template
27
+ const gtmContainerId = env.GTM_CONTAINER_ID;
28
+
29
+ const html = `
30
+ <!DOCTYPE html>
31
+ <html>
32
+ <head>
33
+ <!-- GTM Script -->
34
+ <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
35
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
36
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
37
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
38
+ })(window,document,'script','dataLayer','${gtmContainerId}');</script>
39
+ </head>
40
+ <body>
41
+ <!-- GTM noscript -->
42
+ <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=${gtmContainerId}"
43
+ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
44
+ ...
45
+ </body>
46
+ </html>
47
+ `;
48
+ ```
49
+
50
+ ### 2. Client-Side: Initialize Tracking
51
+
52
+ ```typescript
53
+ import {
54
+ initClickTracker,
55
+ initContextBridgeWithRetry,
56
+ pushPageView,
57
+ syncESIToDataLayer,
58
+ } from '@affectively/aeon-pages-analytics';
59
+
60
+ // Initialize on page load
61
+ initContextBridgeWithRetry({ maxRetries: 3, retryDelayMs: 500 });
62
+ initClickTracker({
63
+ debounceMs: 100,
64
+ maxTextLength: 150,
65
+ excludeSelectors: ['.no-track', '[data-no-track]'],
66
+ includePosition: true,
67
+ });
68
+ pushPageView();
69
+
70
+ // Track SPA navigation
71
+ history.pushState = function(...args) {
72
+ originalPushState(...args);
73
+ syncESIToDataLayer();
74
+ pushPageView();
75
+ };
76
+ ```
77
+
78
+ ### 3. React Provider (Optional)
79
+
80
+ ```tsx
81
+ import { AeonAnalyticsProvider } from '@affectively/aeon-pages-analytics';
82
+
83
+ export default function App({ Component, pageProps }) {
84
+ return (
85
+ <AeonAnalyticsProvider
86
+ gtmContainerId="GTM-XXXXXX"
87
+ trackClicks={true}
88
+ syncESIContext={true}
89
+ >
90
+ <Component {...pageProps} />
91
+ </AeonAnalyticsProvider>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ## Merkle Tree Hashing
97
+
98
+ Each component gets a deterministic hash based on its content:
99
+
100
+ ```typescript
101
+ import { hashNodeSync, buildMerkleTreeSync } from '@affectively/aeon-pages-analytics';
102
+
103
+ // Hash = SHA-256(type + sorted_props + child_hashes)
104
+ const hash = hashNodeSync('Button', { onClick: fn }, ['child1hash']);
105
+ // Returns: "a1b2c3d4e5f6" (12-char truncated hash)
106
+
107
+ // Build tree from component tree
108
+ const merkleTree = buildMerkleTreeSync(componentTree);
109
+ // Map<nodeId, MerkleNode> with hashes and paths
110
+ ```
111
+
112
+ ### Why Merkle Trees?
113
+
114
+ - **Stable IDs** - Same content = same hash, even if DOM order changes
115
+ - **Tree paths** - Full ancestry for every click
116
+ - **Change detection** - Compare trees to find what changed
117
+ - **Cacheable** - Same tree = same hashes
118
+
119
+ ## DataLayer Events
120
+
121
+ ### Context Event (on page load)
122
+ ```javascript
123
+ {
124
+ event: 'aeon.context',
125
+ user: { tier: 'pro', id: 'user_123', sessionId: 'sess_abc' },
126
+ emotion: { primary: 'focused', valence: 0.3, arousal: 0.6 },
127
+ features: { aiInference: true, emotionTracking: true },
128
+ device: { viewport: { width: 1920, height: 1080 }, connection: '4g' },
129
+ time: { localHour: 14, timezone: 'America/New_York' }
130
+ }
131
+ ```
132
+
133
+ ### Click Event
134
+ ```javascript
135
+ {
136
+ event: 'aeon.click',
137
+ aeon: { version: '0.1.0', timestamp: 1707321600000 },
138
+ click: {
139
+ merkleHash: 'a1b2c3d4e5f6',
140
+ treePath: ['root', 'layout', 'header', 'nav', 'settings-button'],
141
+ treePathHashes: ['f1e2d3c4b5a6', 'b2c3d4e5f6a1', ...],
142
+ element: {
143
+ tagName: 'BUTTON',
144
+ text: 'Settings',
145
+ ariaLabel: 'Open settings menu',
146
+ role: 'button'
147
+ },
148
+ position: { x: 1450, y: 32, viewportX: 1450, viewportY: 32 }
149
+ },
150
+ context: {
151
+ userTier: 'pro',
152
+ emotionState: { primary: 'focused', valence: 0.3, arousal: 0.6 },
153
+ sessionId: 'sess_abc'
154
+ }
155
+ }
156
+ ```
157
+
158
+ ### Page View Event
159
+ ```javascript
160
+ {
161
+ event: 'aeon.pageview',
162
+ page: {
163
+ path: '/dashboard',
164
+ title: 'Dashboard - AFFECTIVELY',
165
+ merkleRoot: 'abc123def456'
166
+ },
167
+ user: { tier: 'pro', sessionId: 'sess_abc' }
168
+ }
169
+ ```
170
+
171
+ ## API Reference
172
+
173
+ ### Click Tracking
174
+
175
+ ```typescript
176
+ // Initialize
177
+ initClickTracker(options?: ClickTrackingOptions): void
178
+
179
+ // Stop tracking
180
+ stopClickTracker(): void
181
+
182
+ // Check status
183
+ isClickTrackerActive(): boolean
184
+
185
+ // Manual tracking
186
+ trackClick(element: HTMLElement, event: MouseEvent): void
187
+ trackInteraction(type: string, data: Record<string, unknown>): void
188
+ ```
189
+
190
+ ### Context Bridge
191
+
192
+ ```typescript
193
+ // ESI state access
194
+ getESIState(): ESIState | null
195
+ hasESIState(): boolean
196
+ getESIProperty<K extends keyof ESIState>(key: K): ESIState[K] | null
197
+
198
+ // Sync to dataLayer
199
+ syncESIToDataLayer(): void
200
+ pushPageView(): void
201
+
202
+ // Watch for changes
203
+ watchESIChanges(): () => void
204
+ initContextBridge(): void
205
+ initContextBridgeWithRetry(options: { maxRetries: number, retryDelayMs: number }): void
206
+
207
+ // Utilities
208
+ isAdmin(): boolean
209
+ hasFeature(feature: string): boolean
210
+ meetsTierRequirement(tier: UserTier): boolean
211
+ getUserTier(): UserTier
212
+ getEmotionState(): EmotionState | null
213
+ ```
214
+
215
+ ### Merkle Tree
216
+
217
+ ```typescript
218
+ // Sync hashing (uses djb2)
219
+ hashNodeSync(type: string, props: object, childHashes: string[]): string
220
+ buildMerkleTreeSync(tree: ComponentTree): MerkleTree
221
+
222
+ // Async hashing (uses SHA-256)
223
+ hashNodeAsync(type: string, props: object, childHashes: string[]): Promise<string>
224
+ buildMerkleTree(tree: ComponentTree): Promise<MerkleTree>
225
+
226
+ // DOM helpers
227
+ getMerkleAttributes(node: MerkleNode): Record<string, string>
228
+ parseMerkleFromElement(el: HTMLElement): MerkleNode | null
229
+ findNearestMerkleElement(el: HTMLElement): HTMLElement | null
230
+
231
+ // Verification
232
+ verifyMerkleTree(tree: MerkleTree): boolean
233
+ diffMerkleTrees(a: MerkleTree, b: MerkleTree): MerkleDiff
234
+ ```
235
+
236
+ ### GTM Loader
237
+
238
+ ```typescript
239
+ // Inject GTM
240
+ injectGTM(config: GTMConfig): void
241
+ injectGTMNoScript(containerId: string): void
242
+ initializeGTM(config: GTMConfig): void
243
+
244
+ // SSR helpers
245
+ generateGTMScriptTag(containerId: string): string
246
+ generateGTMNoScriptTag(containerId: string): string
247
+ generateDataLayerScript(initialData: object): string
248
+
249
+ // Status
250
+ isGTMInjected(): boolean
251
+ isGTMReady(): boolean
252
+ waitForGTM(timeout?: number): Promise<void>
253
+ ```
254
+
255
+ ## Configuration
256
+
257
+ ```typescript
258
+ interface AnalyticsConfig {
259
+ // Required: GTM container ID
260
+ gtmContainerId: string; // 'GTM-XXXXXX'
261
+
262
+ // Optional: Customize behavior
263
+ trackClicks?: boolean; // Default: true
264
+ trackPageViews?: boolean; // Default: true
265
+ syncESIContext?: boolean; // Default: true
266
+
267
+ // Click tracking options
268
+ clickOptions?: {
269
+ debounceMs?: number; // Default: 0
270
+ maxTextLength?: number; // Default: 100
271
+ excludeSelectors?: string[]; // e.g., ['.no-track']
272
+ includePosition?: boolean; // Default: true
273
+ };
274
+
275
+ // Data layer customization
276
+ dataLayerName?: string; // Default: 'dataLayer'
277
+ eventPrefix?: string; // Default: 'aeon'
278
+ }
279
+ ```
280
+
281
+ ## ESI State Types
282
+
283
+ ```typescript
284
+ type UserTier = 'free' | 'starter' | 'pro' | 'enterprise' | 'admin';
285
+
286
+ interface ESIState {
287
+ userTier: UserTier;
288
+ isAdmin?: boolean;
289
+ userId?: string;
290
+ sessionId?: string;
291
+ isNewSession?: boolean;
292
+ emotionState?: {
293
+ primary: string;
294
+ valence: number;
295
+ arousal: number;
296
+ };
297
+ preferences?: Record<string, unknown>;
298
+ features?: Record<string, boolean>;
299
+ recentPages?: string[];
300
+ viewport?: { width: number; height: number };
301
+ connection?: string;
302
+ localHour?: number;
303
+ timezone?: string;
304
+ }
305
+ ```
306
+
307
+ ## License
308
+
309
+ MIT
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Build script for @affectively/aeon-pages-analytics
3
+ */
4
+
5
+ import { build } from 'bun';
6
+
7
+ async function runBuild() {
8
+ console.log('Building @affectively/aeon-pages-analytics...');
9
+
10
+ // Build main entry point
11
+ await build({
12
+ entrypoints: ['./src/index.ts'],
13
+ outdir: './dist',
14
+ format: 'esm',
15
+ target: 'browser',
16
+ minify: false,
17
+ sourcemap: 'external',
18
+ external: ['react'],
19
+ });
20
+
21
+ // Build provider separately for /react export
22
+ await build({
23
+ entrypoints: ['./src/provider.tsx'],
24
+ outdir: './dist',
25
+ format: 'esm',
26
+ target: 'browser',
27
+ minify: false,
28
+ sourcemap: 'external',
29
+ external: ['react'],
30
+ });
31
+
32
+ console.log('Build complete!');
33
+ }
34
+
35
+ runBuild().catch(console.error);
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@affectively/aeon-pages-analytics",
3
+ "version": "0.1.0",
4
+ "description": "Automatic click tracking and GTM integration for @affectively/aeon-pages",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./react": {
14
+ "import": "./dist/provider.js",
15
+ "types": "./dist/provider.d.ts"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "build": "bun run build.ts && tsc --declaration --emitDeclarationOnly",
20
+ "dev": "bun --watch ./src/index.ts",
21
+ "test": "bun test",
22
+ "prepublishOnly": "bun run build"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "README.md"
27
+ ],
28
+ "peerDependencies": {
29
+ "react": ">=18.0.0"
30
+ },
31
+ "devDependencies": {
32
+ "typescript": "^5.7.0",
33
+ "@types/react": "^19.0.0",
34
+ "react": "^19.0.0"
35
+ },
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/affectively/aeon-pages"
40
+ },
41
+ "keywords": [
42
+ "aeon",
43
+ "pages",
44
+ "analytics",
45
+ "gtm",
46
+ "google-tag-manager",
47
+ "click-tracking",
48
+ "merkle-tree"
49
+ ]
50
+ }