@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.
- package/CHANGELOG.md +112 -0
- package/README.md +625 -0
- package/examples/basic/aeon.config.ts +39 -0
- package/examples/basic/components/Cursor.tsx +86 -0
- package/examples/basic/components/OfflineIndicator.tsx +103 -0
- package/examples/basic/components/PresenceBar.tsx +77 -0
- package/examples/basic/package.json +20 -0
- package/examples/basic/pages/index.tsx +80 -0
- package/package.json +101 -0
- package/packages/analytics/README.md +309 -0
- package/packages/analytics/build.ts +35 -0
- package/packages/analytics/package.json +50 -0
- package/packages/analytics/src/click-tracker.ts +368 -0
- package/packages/analytics/src/context-bridge.ts +319 -0
- package/packages/analytics/src/data-layer.ts +302 -0
- package/packages/analytics/src/gtm-loader.ts +239 -0
- package/packages/analytics/src/index.ts +230 -0
- package/packages/analytics/src/merkle-tree.ts +489 -0
- package/packages/analytics/src/provider.tsx +300 -0
- package/packages/analytics/src/types.ts +320 -0
- package/packages/analytics/src/use-analytics.ts +296 -0
- package/packages/analytics/tsconfig.json +19 -0
- package/packages/benchmarks/src/benchmark.test.ts +691 -0
- package/packages/cli/dist/index.js +61899 -0
- package/packages/cli/package.json +43 -0
- package/packages/cli/src/commands/build.test.ts +682 -0
- package/packages/cli/src/commands/build.ts +890 -0
- package/packages/cli/src/commands/dev.ts +473 -0
- package/packages/cli/src/commands/init.ts +409 -0
- package/packages/cli/src/commands/start.ts +297 -0
- package/packages/cli/src/index.ts +105 -0
- package/packages/directives/src/use-aeon.ts +272 -0
- package/packages/mcp-server/package.json +51 -0
- package/packages/mcp-server/src/index.ts +178 -0
- package/packages/mcp-server/src/resources.ts +346 -0
- package/packages/mcp-server/src/tools/index.ts +36 -0
- package/packages/mcp-server/src/tools/navigation.ts +545 -0
- package/packages/mcp-server/tsconfig.json +21 -0
- package/packages/react/package.json +40 -0
- package/packages/react/src/Link.tsx +388 -0
- package/packages/react/src/components/InstallPrompt.tsx +286 -0
- package/packages/react/src/components/OfflineDiagnostics.tsx +677 -0
- package/packages/react/src/components/PushNotifications.tsx +453 -0
- package/packages/react/src/hooks/useAeonNavigation.ts +219 -0
- package/packages/react/src/hooks/useConflicts.ts +277 -0
- package/packages/react/src/hooks/useNetworkState.ts +209 -0
- package/packages/react/src/hooks/usePilotNavigation.ts +254 -0
- package/packages/react/src/hooks/useServiceWorker.ts +278 -0
- package/packages/react/src/hooks.ts +195 -0
- package/packages/react/src/index.ts +151 -0
- package/packages/react/src/provider.tsx +467 -0
- package/packages/react/tsconfig.json +19 -0
- package/packages/runtime/README.md +399 -0
- package/packages/runtime/build.ts +48 -0
- package/packages/runtime/package.json +71 -0
- package/packages/runtime/schema.sql +40 -0
- package/packages/runtime/src/api-routes.ts +465 -0
- package/packages/runtime/src/benchmark.ts +171 -0
- package/packages/runtime/src/cache.ts +479 -0
- package/packages/runtime/src/durable-object.ts +1341 -0
- package/packages/runtime/src/index.ts +360 -0
- package/packages/runtime/src/navigation.test.ts +421 -0
- package/packages/runtime/src/navigation.ts +422 -0
- package/packages/runtime/src/nextjs-adapter.ts +272 -0
- package/packages/runtime/src/offline/encrypted-queue.test.ts +607 -0
- package/packages/runtime/src/offline/encrypted-queue.ts +478 -0
- package/packages/runtime/src/offline/encryption.test.ts +412 -0
- package/packages/runtime/src/offline/encryption.ts +397 -0
- package/packages/runtime/src/offline/types.ts +465 -0
- package/packages/runtime/src/predictor.ts +371 -0
- package/packages/runtime/src/registry.ts +351 -0
- package/packages/runtime/src/router/context-extractor.ts +661 -0
- package/packages/runtime/src/router/esi-control-react.tsx +2053 -0
- package/packages/runtime/src/router/esi-control.ts +541 -0
- package/packages/runtime/src/router/esi-cyrano.ts +779 -0
- package/packages/runtime/src/router/esi-format-react.tsx +1744 -0
- package/packages/runtime/src/router/esi-react.tsx +1065 -0
- package/packages/runtime/src/router/esi-translate-observer.ts +476 -0
- package/packages/runtime/src/router/esi-translate-react.tsx +556 -0
- package/packages/runtime/src/router/esi-translate.ts +503 -0
- package/packages/runtime/src/router/esi.ts +666 -0
- package/packages/runtime/src/router/heuristic-adapter.test.ts +295 -0
- package/packages/runtime/src/router/heuristic-adapter.ts +557 -0
- package/packages/runtime/src/router/index.ts +298 -0
- package/packages/runtime/src/router/merkle-capability.ts +473 -0
- package/packages/runtime/src/router/speculation.ts +451 -0
- package/packages/runtime/src/router/types.ts +630 -0
- package/packages/runtime/src/router.test.ts +470 -0
- package/packages/runtime/src/router.ts +302 -0
- package/packages/runtime/src/server.ts +481 -0
- package/packages/runtime/src/service-worker-push.ts +319 -0
- package/packages/runtime/src/service-worker.ts +553 -0
- package/packages/runtime/src/skeleton-hydrate.ts +237 -0
- package/packages/runtime/src/speculation.test.ts +389 -0
- package/packages/runtime/src/speculation.ts +486 -0
- package/packages/runtime/src/storage.test.ts +1297 -0
- package/packages/runtime/src/storage.ts +1048 -0
- package/packages/runtime/src/sync/conflict-resolver.test.ts +528 -0
- package/packages/runtime/src/sync/conflict-resolver.ts +565 -0
- package/packages/runtime/src/sync/coordinator.test.ts +608 -0
- package/packages/runtime/src/sync/coordinator.ts +596 -0
- package/packages/runtime/src/tree-compiler.ts +295 -0
- package/packages/runtime/src/types.ts +728 -0
- package/packages/runtime/src/worker.ts +327 -0
- package/packages/runtime/tsconfig.json +20 -0
- package/packages/runtime/wasm/aeon_pages_runtime.d.ts +504 -0
- package/packages/runtime/wasm/aeon_pages_runtime.js +1657 -0
- package/packages/runtime/wasm/aeon_pages_runtime_bg.wasm +0 -0
- package/packages/runtime/wasm/aeon_pages_runtime_bg.wasm.d.ts +196 -0
- package/packages/runtime/wasm/package.json +21 -0
- package/packages/runtime/wrangler.toml +41 -0
- package/packages/runtime-wasm/Cargo.lock +436 -0
- package/packages/runtime-wasm/Cargo.toml +29 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime.d.ts +480 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime.js +1568 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm +0 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm.d.ts +192 -0
- package/packages/runtime-wasm/pkg/package.json +21 -0
- package/packages/runtime-wasm/src/hydrate.rs +352 -0
- package/packages/runtime-wasm/src/lib.rs +191 -0
- package/packages/runtime-wasm/src/render.rs +629 -0
- package/packages/runtime-wasm/src/router.rs +298 -0
- package/packages/runtime-wasm/src/skeleton.rs +430 -0
- package/rfcs/RFC-001-ZERO-DEPENDENCY-RENDERING.md +1446 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Hook for Aeon Analytics
|
|
3
|
+
*
|
|
4
|
+
* Initializes GTM, syncs ESI context, and starts click tracking.
|
|
5
|
+
* One hook to rule them all.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useEffect, useRef, useCallback } from 'react';
|
|
9
|
+
import type { AnalyticsConfig, MerkleTree } from './types';
|
|
10
|
+
import { initializeGTM, waitForGTM } from './gtm-loader';
|
|
11
|
+
import { initContextBridge, pushPageView, getESIState } from './context-bridge';
|
|
12
|
+
import {
|
|
13
|
+
initClickTracker,
|
|
14
|
+
trackClick,
|
|
15
|
+
trackInteraction,
|
|
16
|
+
} from './click-tracker';
|
|
17
|
+
import { setDebugMode } from './data-layer';
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Hook Return Type
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
export interface UseAnalyticsReturn {
|
|
24
|
+
/** Whether analytics is initialized */
|
|
25
|
+
isInitialized: boolean;
|
|
26
|
+
|
|
27
|
+
/** Whether GTM is loaded and ready */
|
|
28
|
+
isGTMReady: boolean;
|
|
29
|
+
|
|
30
|
+
/** Manually track a click on an element */
|
|
31
|
+
trackClick: (element: HTMLElement, event?: MouseEvent) => void;
|
|
32
|
+
|
|
33
|
+
/** Track a custom interaction */
|
|
34
|
+
trackInteraction: (
|
|
35
|
+
name: string,
|
|
36
|
+
data: Record<string, unknown>,
|
|
37
|
+
element?: HTMLElement,
|
|
38
|
+
) => void;
|
|
39
|
+
|
|
40
|
+
/** Manually push a page view */
|
|
41
|
+
pushPageView: (merkleRoot?: string) => void;
|
|
42
|
+
|
|
43
|
+
/** Set the current Merkle tree (for page view tracking) */
|
|
44
|
+
setMerkleTree: (tree: MerkleTree) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Main Hook
|
|
49
|
+
// ============================================================================
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Initialize Aeon Analytics with automatic click tracking
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```tsx
|
|
56
|
+
* function App() {
|
|
57
|
+
* useAeonAnalytics({
|
|
58
|
+
* gtmContainerId: 'GTM-XXXXXX',
|
|
59
|
+
* trackClicks: true,
|
|
60
|
+
* syncESIContext: true,
|
|
61
|
+
* });
|
|
62
|
+
*
|
|
63
|
+
* return <div>My App</div>;
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export function useAeonAnalytics(config: AnalyticsConfig): UseAnalyticsReturn {
|
|
68
|
+
const initializedRef = useRef(false);
|
|
69
|
+
const gtmReadyRef = useRef(false);
|
|
70
|
+
const cleanupRef = useRef<(() => void) | null>(null);
|
|
71
|
+
const merkleTreeRef = useRef<MerkleTree | null>(null);
|
|
72
|
+
const configRef = useRef(config);
|
|
73
|
+
|
|
74
|
+
// Keep config ref updated
|
|
75
|
+
configRef.current = config;
|
|
76
|
+
|
|
77
|
+
// Initialize on mount
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
// Prevent double initialization
|
|
80
|
+
if (initializedRef.current || window.__AEON_ANALYTICS_INITIALIZED__) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
initializedRef.current = true;
|
|
85
|
+
window.__AEON_ANALYTICS_INITIALIZED__ = true;
|
|
86
|
+
|
|
87
|
+
// Enable debug mode if configured
|
|
88
|
+
if (config.debug) {
|
|
89
|
+
setDebugMode(true);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const cleanupFunctions: (() => void)[] = [];
|
|
93
|
+
|
|
94
|
+
// 1. Initialize GTM
|
|
95
|
+
if (config.gtmContainerId) {
|
|
96
|
+
initializeGTM({
|
|
97
|
+
containerId: config.gtmContainerId,
|
|
98
|
+
dataLayerName: config.dataLayerName,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Wait for GTM to be ready
|
|
102
|
+
waitForGTM().then((ready) => {
|
|
103
|
+
gtmReadyRef.current = ready;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 2. Initialize ESI context bridge
|
|
108
|
+
if (config.syncESIContext !== false) {
|
|
109
|
+
const cleanup = initContextBridge({
|
|
110
|
+
dataLayerName: config.dataLayerName,
|
|
111
|
+
eventPrefix: config.eventPrefix,
|
|
112
|
+
syncESIContext: config.syncESIContext,
|
|
113
|
+
});
|
|
114
|
+
cleanupFunctions.push(cleanup);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 3. Initialize click tracking
|
|
118
|
+
if (config.trackClicks !== false) {
|
|
119
|
+
const cleanup = initClickTracker(config);
|
|
120
|
+
cleanupFunctions.push(cleanup);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 4. Push initial page view
|
|
124
|
+
if (config.trackPageViews !== false) {
|
|
125
|
+
const merkleRoot = merkleTreeRef.current?.rootHash || '';
|
|
126
|
+
pushPageView(
|
|
127
|
+
{
|
|
128
|
+
dataLayerName: config.dataLayerName,
|
|
129
|
+
eventPrefix: config.eventPrefix,
|
|
130
|
+
},
|
|
131
|
+
merkleRoot,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Store cleanup function
|
|
136
|
+
cleanupRef.current = () => {
|
|
137
|
+
for (const cleanup of cleanupFunctions) {
|
|
138
|
+
cleanup();
|
|
139
|
+
}
|
|
140
|
+
window.__AEON_ANALYTICS_INITIALIZED__ = false;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Cleanup on unmount
|
|
144
|
+
return () => {
|
|
145
|
+
if (cleanupRef.current) {
|
|
146
|
+
cleanupRef.current();
|
|
147
|
+
cleanupRef.current = null;
|
|
148
|
+
}
|
|
149
|
+
initializedRef.current = false;
|
|
150
|
+
};
|
|
151
|
+
}, []); // Only run once on mount
|
|
152
|
+
|
|
153
|
+
// Handle route changes
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
if (!initializedRef.current || config.trackPageViews === false) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Push page view on pathname change
|
|
160
|
+
const merkleRoot = merkleTreeRef.current?.rootHash || '';
|
|
161
|
+
pushPageView(
|
|
162
|
+
{
|
|
163
|
+
dataLayerName: config.dataLayerName,
|
|
164
|
+
eventPrefix: config.eventPrefix,
|
|
165
|
+
},
|
|
166
|
+
merkleRoot,
|
|
167
|
+
);
|
|
168
|
+
}, [typeof window !== 'undefined' ? window.location.pathname : '']);
|
|
169
|
+
|
|
170
|
+
// Manual track click
|
|
171
|
+
const handleTrackClick = useCallback(
|
|
172
|
+
(element: HTMLElement, event?: MouseEvent) => {
|
|
173
|
+
trackClick(element, event, {
|
|
174
|
+
dataLayerName: configRef.current.dataLayerName,
|
|
175
|
+
eventPrefix: configRef.current.eventPrefix,
|
|
176
|
+
clickOptions: configRef.current.clickOptions,
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
[],
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// Manual track interaction
|
|
183
|
+
const handleTrackInteraction = useCallback(
|
|
184
|
+
(name: string, data: Record<string, unknown>, element?: HTMLElement) => {
|
|
185
|
+
trackInteraction(name, data, element, {
|
|
186
|
+
dataLayerName: configRef.current.dataLayerName,
|
|
187
|
+
eventPrefix: configRef.current.eventPrefix,
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
[],
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Manual push page view
|
|
194
|
+
const handlePushPageView = useCallback((merkleRoot?: string) => {
|
|
195
|
+
pushPageView(
|
|
196
|
+
{
|
|
197
|
+
dataLayerName: configRef.current.dataLayerName,
|
|
198
|
+
eventPrefix: configRef.current.eventPrefix,
|
|
199
|
+
},
|
|
200
|
+
merkleRoot || merkleTreeRef.current?.rootHash || '',
|
|
201
|
+
);
|
|
202
|
+
}, []);
|
|
203
|
+
|
|
204
|
+
// Set Merkle tree
|
|
205
|
+
const handleSetMerkleTree = useCallback((tree: MerkleTree) => {
|
|
206
|
+
merkleTreeRef.current = tree;
|
|
207
|
+
window.__AEON_MERKLE_TREE__ = tree;
|
|
208
|
+
}, []);
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
isInitialized: initializedRef.current,
|
|
212
|
+
isGTMReady: gtmReadyRef.current,
|
|
213
|
+
trackClick: handleTrackClick,
|
|
214
|
+
trackInteraction: handleTrackInteraction,
|
|
215
|
+
pushPageView: handlePushPageView,
|
|
216
|
+
setMerkleTree: handleSetMerkleTree,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ============================================================================
|
|
221
|
+
// Utility Hooks
|
|
222
|
+
// ============================================================================
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get current ESI state in a component
|
|
226
|
+
*/
|
|
227
|
+
export function useESIState() {
|
|
228
|
+
return getESIState();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Track when a component mounts/unmounts
|
|
233
|
+
*/
|
|
234
|
+
export function useTrackMount(
|
|
235
|
+
componentName: string,
|
|
236
|
+
config?: Pick<AnalyticsConfig, 'dataLayerName' | 'eventPrefix'>,
|
|
237
|
+
) {
|
|
238
|
+
useEffect(() => {
|
|
239
|
+
trackInteraction(
|
|
240
|
+
'component.mount',
|
|
241
|
+
{ component: componentName },
|
|
242
|
+
undefined,
|
|
243
|
+
config,
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
return () => {
|
|
247
|
+
trackInteraction(
|
|
248
|
+
'component.unmount',
|
|
249
|
+
{ component: componentName },
|
|
250
|
+
undefined,
|
|
251
|
+
config,
|
|
252
|
+
);
|
|
253
|
+
};
|
|
254
|
+
}, [componentName]);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Track visibility of an element using IntersectionObserver
|
|
259
|
+
*/
|
|
260
|
+
export function useTrackVisibility(
|
|
261
|
+
ref: React.RefObject<HTMLElement>,
|
|
262
|
+
componentName: string,
|
|
263
|
+
config?: Pick<AnalyticsConfig, 'dataLayerName' | 'eventPrefix'>,
|
|
264
|
+
) {
|
|
265
|
+
const hasTracked = useRef(false);
|
|
266
|
+
|
|
267
|
+
useEffect(() => {
|
|
268
|
+
if (!ref.current || hasTracked.current) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const observer = new IntersectionObserver(
|
|
273
|
+
(entries) => {
|
|
274
|
+
for (const entry of entries) {
|
|
275
|
+
if (entry.isIntersecting && !hasTracked.current) {
|
|
276
|
+
hasTracked.current = true;
|
|
277
|
+
trackInteraction(
|
|
278
|
+
'component.visible',
|
|
279
|
+
{ component: componentName },
|
|
280
|
+
ref.current || undefined,
|
|
281
|
+
config,
|
|
282
|
+
);
|
|
283
|
+
observer.disconnect();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
{ threshold: 0.5 },
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
observer.observe(ref.current);
|
|
291
|
+
|
|
292
|
+
return () => {
|
|
293
|
+
observer.disconnect();
|
|
294
|
+
};
|
|
295
|
+
}, [componentName]);
|
|
296
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ESNext", "DOM"],
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationDir": "./dist",
|
|
9
|
+
"emitDeclarationOnly": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
"outDir": "./dist",
|
|
15
|
+
"rootDir": "./src"
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
19
|
+
}
|