@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,277 @@
1
+ /**
2
+ * useConflicts Hook
3
+ *
4
+ * React hook for managing conflicts in offline-first applications.
5
+ * Provides access to conflict list, resolution methods, and statistics.
6
+ */
7
+
8
+ import { useState, useEffect, useCallback, useMemo } from 'react';
9
+
10
+ // ============================================================================
11
+ // Types
12
+ // ============================================================================
13
+
14
+ export type ResolutionStrategy =
15
+ | 'local-wins'
16
+ | 'remote-wins'
17
+ | 'merge'
18
+ | 'last-modified'
19
+ | 'manual';
20
+
21
+ export interface Conflict {
22
+ id: string;
23
+ operationId: string;
24
+ sessionId: string;
25
+ localData: Record<string, unknown>;
26
+ remoteData: Record<string, unknown>;
27
+ type: 'update_update' | 'delete_update' | 'update_delete' | 'concurrent';
28
+ severity: 'low' | 'medium' | 'high';
29
+ detectedAt: number;
30
+ resolution?: {
31
+ strategy: ResolutionStrategy;
32
+ resolvedData: Record<string, unknown>;
33
+ resolvedAt: number;
34
+ resolvedBy?: string;
35
+ };
36
+ }
37
+
38
+ export interface ConflictStats {
39
+ total: number;
40
+ unresolved: number;
41
+ highSeverity: number;
42
+ byType: Record<Conflict['type'], number>;
43
+ byStrategy: Record<ResolutionStrategy, number>;
44
+ }
45
+
46
+ export interface UseConflictsResult {
47
+ /** All conflicts */
48
+ conflicts: Conflict[];
49
+ /** Unresolved conflicts only */
50
+ unresolvedConflicts: Conflict[];
51
+ /** High severity conflicts */
52
+ highSeverityConflicts: Conflict[];
53
+ /** Conflict statistics */
54
+ stats: ConflictStats;
55
+ /** Resolve a conflict */
56
+ resolveConflict: (
57
+ conflictId: string,
58
+ strategy: ResolutionStrategy,
59
+ customData?: Record<string, unknown>,
60
+ ) => Promise<void>;
61
+ /** Dismiss a conflict (remove without resolution) */
62
+ dismissConflict: (conflictId: string) => void;
63
+ /** Clear all resolved conflicts */
64
+ clearResolved: () => void;
65
+ /** Refresh conflicts from source */
66
+ refresh: () => void;
67
+ /** Whether conflicts are loading */
68
+ isLoading: boolean;
69
+ }
70
+
71
+ // ============================================================================
72
+ // Mock conflict resolver (would connect to actual ConflictResolver in production)
73
+ // ============================================================================
74
+
75
+ const conflictStore: Map<string, Conflict> = new Map();
76
+ const listeners: Set<() => void> = new Set();
77
+
78
+ function notifyListeners() {
79
+ listeners.forEach((listener) => listener());
80
+ }
81
+
82
+ // ============================================================================
83
+ // Hook Implementation
84
+ // ============================================================================
85
+
86
+ /**
87
+ * Hook to manage conflicts in offline-first applications
88
+ */
89
+ export function useConflicts(sessionId?: string): UseConflictsResult {
90
+ const [conflicts, setConflicts] = useState<Conflict[]>([]);
91
+ const [isLoading, setIsLoading] = useState(false);
92
+
93
+ const loadConflicts = useCallback(() => {
94
+ const allConflicts = Array.from(conflictStore.values());
95
+ const filtered = sessionId
96
+ ? allConflicts.filter((c) => c.sessionId === sessionId)
97
+ : allConflicts;
98
+ setConflicts(filtered);
99
+ }, [sessionId]);
100
+
101
+ useEffect(() => {
102
+ loadConflicts();
103
+
104
+ const listener = () => loadConflicts();
105
+ listeners.add(listener);
106
+
107
+ return () => {
108
+ listeners.delete(listener);
109
+ };
110
+ }, [loadConflicts]);
111
+
112
+ const unresolvedConflicts = useMemo(
113
+ () => conflicts.filter((c) => !c.resolution),
114
+ [conflicts],
115
+ );
116
+
117
+ const highSeverityConflicts = useMemo(
118
+ () => conflicts.filter((c) => !c.resolution && c.severity === 'high'),
119
+ [conflicts],
120
+ );
121
+
122
+ const stats = useMemo<ConflictStats>(() => {
123
+ const byType: Record<Conflict['type'], number> = {
124
+ update_update: 0,
125
+ delete_update: 0,
126
+ update_delete: 0,
127
+ concurrent: 0,
128
+ };
129
+
130
+ const byStrategy: Record<ResolutionStrategy, number> = {
131
+ 'local-wins': 0,
132
+ 'remote-wins': 0,
133
+ merge: 0,
134
+ 'last-modified': 0,
135
+ manual: 0,
136
+ };
137
+
138
+ let unresolved = 0;
139
+ let highSeverity = 0;
140
+
141
+ for (const conflict of conflicts) {
142
+ byType[conflict.type]++;
143
+
144
+ if (!conflict.resolution) {
145
+ unresolved++;
146
+ if (conflict.severity === 'high') {
147
+ highSeverity++;
148
+ }
149
+ } else {
150
+ byStrategy[conflict.resolution.strategy]++;
151
+ }
152
+ }
153
+
154
+ return {
155
+ total: conflicts.length,
156
+ unresolved,
157
+ highSeverity,
158
+ byType,
159
+ byStrategy,
160
+ };
161
+ }, [conflicts]);
162
+
163
+ const resolveConflict = useCallback(
164
+ async (
165
+ conflictId: string,
166
+ strategy: ResolutionStrategy,
167
+ customData?: Record<string, unknown>,
168
+ ) => {
169
+ setIsLoading(true);
170
+
171
+ try {
172
+ const conflict = conflictStore.get(conflictId);
173
+ if (!conflict) {
174
+ throw new Error(`Conflict ${conflictId} not found`);
175
+ }
176
+
177
+ let resolvedData: Record<string, unknown>;
178
+
179
+ switch (strategy) {
180
+ case 'local-wins':
181
+ resolvedData = conflict.localData;
182
+ break;
183
+ case 'remote-wins':
184
+ resolvedData = conflict.remoteData;
185
+ break;
186
+ case 'merge':
187
+ // Simple merge - combine both, local wins for conflicts
188
+ resolvedData = { ...conflict.remoteData, ...conflict.localData };
189
+ break;
190
+ case 'last-modified':
191
+ // Default to local
192
+ resolvedData = conflict.localData;
193
+ break;
194
+ case 'manual':
195
+ if (!customData) {
196
+ throw new Error('Manual resolution requires customData');
197
+ }
198
+ resolvedData = customData;
199
+ break;
200
+ default:
201
+ resolvedData = conflict.localData;
202
+ }
203
+
204
+ conflict.resolution = {
205
+ strategy,
206
+ resolvedData,
207
+ resolvedAt: Date.now(),
208
+ };
209
+
210
+ conflictStore.set(conflictId, conflict);
211
+ notifyListeners();
212
+ } finally {
213
+ setIsLoading(false);
214
+ }
215
+ },
216
+ [],
217
+ );
218
+
219
+ const dismissConflict = useCallback((conflictId: string) => {
220
+ conflictStore.delete(conflictId);
221
+ notifyListeners();
222
+ }, []);
223
+
224
+ const clearResolved = useCallback(() => {
225
+ for (const [id, conflict] of conflictStore) {
226
+ if (conflict.resolution) {
227
+ conflictStore.delete(id);
228
+ }
229
+ }
230
+ notifyListeners();
231
+ }, []);
232
+
233
+ const refresh = useCallback(() => {
234
+ loadConflicts();
235
+ }, [loadConflicts]);
236
+
237
+ return {
238
+ conflicts,
239
+ unresolvedConflicts,
240
+ highSeverityConflicts,
241
+ stats,
242
+ resolveConflict,
243
+ dismissConflict,
244
+ clearResolved,
245
+ refresh,
246
+ isLoading,
247
+ };
248
+ }
249
+
250
+ // ============================================================================
251
+ // Helper functions for external use
252
+ // ============================================================================
253
+
254
+ /**
255
+ * Add a conflict to the store (for testing or external integration)
256
+ */
257
+ export function addConflict(conflict: Conflict): void {
258
+ conflictStore.set(conflict.id, conflict);
259
+ notifyListeners();
260
+ }
261
+
262
+ /**
263
+ * Get all conflicts from the store
264
+ */
265
+ export function getAllConflicts(): Conflict[] {
266
+ return Array.from(conflictStore.values());
267
+ }
268
+
269
+ /**
270
+ * Clear all conflicts
271
+ */
272
+ export function clearAllConflicts(): void {
273
+ conflictStore.clear();
274
+ notifyListeners();
275
+ }
276
+
277
+ export default useConflicts;
@@ -0,0 +1,209 @@
1
+ /**
2
+ * useNetworkState Hook
3
+ *
4
+ * React hook for monitoring network state and bandwidth.
5
+ * Provides real-time updates on connection quality and effective type.
6
+ */
7
+
8
+ import { useState, useEffect, useCallback } from 'react';
9
+
10
+ // ============================================================================
11
+ // Types
12
+ // ============================================================================
13
+
14
+ export type NetworkState = 'online' | 'offline' | 'poor' | 'unknown';
15
+
16
+ export interface BandwidthProfile {
17
+ /** Estimated bandwidth in Kbps */
18
+ speedKbps: number;
19
+ /** Estimated latency in ms */
20
+ latencyMs: number;
21
+ /** Reliability score (0-1) */
22
+ reliability: number;
23
+ /** Effective connection type */
24
+ effectiveType: '2g' | '3g' | '4g' | 'slow-2g' | 'unknown';
25
+ }
26
+
27
+ export interface NetworkStateResult {
28
+ /** Current network state */
29
+ state: NetworkState;
30
+ /** Whether currently online */
31
+ isOnline: boolean;
32
+ /** Whether on poor connection */
33
+ isPoor: boolean;
34
+ /** Bandwidth profile */
35
+ bandwidth: BandwidthProfile;
36
+ /** Time since last state change */
37
+ timeSinceChange: number;
38
+ /** Force refresh network state */
39
+ refresh: () => void;
40
+ }
41
+
42
+ // Network Information API types
43
+ interface NetworkInformation {
44
+ effectiveType?: '2g' | '3g' | '4g' | 'slow-2g';
45
+ downlink?: number;
46
+ rtt?: number;
47
+ saveData?: boolean;
48
+ addEventListener?: (event: string, callback: () => void) => void;
49
+ removeEventListener?: (event: string, callback: () => void) => void;
50
+ }
51
+
52
+ interface NavigatorWithConnection extends Navigator {
53
+ connection?: NetworkInformation;
54
+ mozConnection?: NetworkInformation;
55
+ webkitConnection?: NetworkInformation;
56
+ }
57
+
58
+ // ============================================================================
59
+ // Hook Implementation
60
+ // ============================================================================
61
+
62
+ /**
63
+ * Hook to monitor network state and bandwidth
64
+ */
65
+ export function useNetworkState(): NetworkStateResult {
66
+ const [state, setState] = useState<NetworkState>('unknown');
67
+ const [lastChange, setLastChange] = useState(Date.now());
68
+ const [bandwidth, setBandwidth] = useState<BandwidthProfile>({
69
+ speedKbps: 1024,
70
+ latencyMs: 50,
71
+ reliability: 1,
72
+ effectiveType: 'unknown',
73
+ });
74
+
75
+ const getConnection = useCallback((): NetworkInformation | undefined => {
76
+ if (typeof navigator === 'undefined') return undefined;
77
+ const nav = navigator as NavigatorWithConnection;
78
+ return nav.connection || nav.mozConnection || nav.webkitConnection;
79
+ }, []);
80
+
81
+ const updateBandwidth = useCallback(() => {
82
+ const conn = getConnection();
83
+ if (!conn) return;
84
+
85
+ const effectiveType = conn.effectiveType || 'unknown';
86
+ let speedKbps = 1024;
87
+ let latencyMs = 50;
88
+ let reliability = 1;
89
+
90
+ switch (effectiveType) {
91
+ case 'slow-2g':
92
+ speedKbps = 50;
93
+ latencyMs = 2000;
94
+ reliability = 0.5;
95
+ break;
96
+ case '2g':
97
+ speedKbps = 150;
98
+ latencyMs = 1000;
99
+ reliability = 0.7;
100
+ break;
101
+ case '3g':
102
+ speedKbps = 750;
103
+ latencyMs = 400;
104
+ reliability = 0.85;
105
+ break;
106
+ case '4g':
107
+ speedKbps = 5000;
108
+ latencyMs = 50;
109
+ reliability = 0.95;
110
+ break;
111
+ }
112
+
113
+ // Use actual values if available
114
+ if (conn.downlink) {
115
+ speedKbps = conn.downlink * 1024;
116
+ }
117
+ if (conn.rtt) {
118
+ latencyMs = conn.rtt;
119
+ }
120
+
121
+ setBandwidth({
122
+ speedKbps,
123
+ latencyMs,
124
+ reliability,
125
+ effectiveType,
126
+ });
127
+
128
+ // Update state based on connection quality
129
+ if (effectiveType === 'slow-2g' || effectiveType === '2g') {
130
+ setState((prev) => {
131
+ if (prev !== 'poor') {
132
+ setLastChange(Date.now());
133
+ }
134
+ return 'poor';
135
+ });
136
+ }
137
+ }, [getConnection]);
138
+
139
+ const updateOnlineState = useCallback(() => {
140
+ if (typeof navigator === 'undefined') return;
141
+
142
+ const isOnline = navigator.onLine;
143
+ setState((prev) => {
144
+ const newState = isOnline ? 'online' : 'offline';
145
+ if (prev !== newState) {
146
+ setLastChange(Date.now());
147
+ }
148
+ return newState;
149
+ });
150
+
151
+ if (isOnline) {
152
+ updateBandwidth();
153
+ }
154
+ }, [updateBandwidth]);
155
+
156
+ const refresh = useCallback(() => {
157
+ updateOnlineState();
158
+ }, [updateOnlineState]);
159
+
160
+ useEffect(() => {
161
+ // Initial state
162
+ updateOnlineState();
163
+
164
+ // Listen for online/offline events
165
+ const handleOnline = () => {
166
+ setState('online');
167
+ setLastChange(Date.now());
168
+ updateBandwidth();
169
+ };
170
+
171
+ const handleOffline = () => {
172
+ setState('offline');
173
+ setLastChange(Date.now());
174
+ };
175
+
176
+ if (typeof window !== 'undefined') {
177
+ window.addEventListener('online', handleOnline);
178
+ window.addEventListener('offline', handleOffline);
179
+ }
180
+
181
+ // Listen for connection changes
182
+ const conn = getConnection();
183
+ if (conn?.addEventListener) {
184
+ conn.addEventListener('change', updateBandwidth);
185
+ }
186
+
187
+ return () => {
188
+ if (typeof window !== 'undefined') {
189
+ window.removeEventListener('online', handleOnline);
190
+ window.removeEventListener('offline', handleOffline);
191
+ }
192
+
193
+ if (conn?.removeEventListener) {
194
+ conn.removeEventListener('change', updateBandwidth);
195
+ }
196
+ };
197
+ }, [getConnection, updateBandwidth, updateOnlineState]);
198
+
199
+ return {
200
+ state,
201
+ isOnline: state === 'online' || state === 'poor',
202
+ isPoor: state === 'poor',
203
+ bandwidth,
204
+ timeSinceChange: Date.now() - lastChange,
205
+ refresh,
206
+ };
207
+ }
208
+
209
+ export default useNetworkState;