@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,327 @@
1
+ /**
2
+ * Aeon Pages Cloudflare Worker
3
+ *
4
+ * Entry point for Cloudflare Workers deployment.
5
+ * Handles routing to Durable Objects, API routes, and static assets.
6
+ */
7
+
8
+ // Export Durable Object classes
9
+ export { AeonPageSession, AeonRoutesRegistry } from './durable-object';
10
+
11
+ // Import API router
12
+ import { ApiRouter, createApiRouter } from './api-routes';
13
+ import type {
14
+ AeonEnv,
15
+ ApiRouteModule,
16
+ ExecutionContext,
17
+ DurableObjectNamespace,
18
+ } from './types';
19
+
20
+ // =============================================================================
21
+ // WORKER FACTORY
22
+ // =============================================================================
23
+
24
+ /** Options for creating an Aeon worker */
25
+ export interface AeonWorkerOptions<E extends AeonEnv = AeonEnv> {
26
+ /** API routes to register */
27
+ apiRoutes?: Record<string, ApiRouteModule<E>>;
28
+
29
+ /** CORS configuration */
30
+ cors?: {
31
+ origin?: string | string[];
32
+ methods?: string[];
33
+ headers?: string[];
34
+ credentials?: boolean;
35
+ };
36
+
37
+ /** Custom fetch handler to run before Aeon routing */
38
+ onRequest?: (
39
+ request: Request,
40
+ env: E,
41
+ ctx: ExecutionContext,
42
+ ) => Promise<Response | null>;
43
+
44
+ /** Custom 404 handler */
45
+ notFound?: (request: Request, env: E) => Response | Promise<Response>;
46
+ }
47
+
48
+ /**
49
+ * Create an Aeon worker with API route support
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * import { createAeonWorker } from '@affectively/aeon-pages-runtime/worker';
54
+ *
55
+ * export default createAeonWorker({
56
+ * apiRoutes: {
57
+ * '/api/chat': {
58
+ * POST: async ({ request, env }) => {
59
+ * const body = await request.json();
60
+ * const result = await env.AI.run('@cf/meta/llama-3-8b-instruct', { ... });
61
+ * return Response.json({ result });
62
+ * },
63
+ * },
64
+ * '/api/users/[id]': {
65
+ * GET: async ({ params, env }) => {
66
+ * const user = await env.DB.prepare('SELECT * FROM users WHERE id = ?')
67
+ * .bind(params.id)
68
+ * .first();
69
+ * return Response.json(user);
70
+ * },
71
+ * },
72
+ * },
73
+ * });
74
+ * ```
75
+ */
76
+ export function createAeonWorker<E extends AeonEnv = AeonEnv>(
77
+ options: AeonWorkerOptions<E> = {},
78
+ ): ExportedHandler<E> {
79
+ // Create API router and register routes
80
+ const apiRouter = createApiRouter<E>();
81
+ if (options.apiRoutes) {
82
+ apiRouter.registerAll(options.apiRoutes);
83
+ }
84
+
85
+ // Build CORS headers
86
+ const corsConfig = {
87
+ origin: '*',
88
+ methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
89
+ headers: ['Content-Type', 'Authorization'],
90
+ credentials: false,
91
+ ...options.cors,
92
+ };
93
+
94
+ const getCorsHeaders = (
95
+ requestOrigin?: string | null,
96
+ ): Record<string, string> => {
97
+ let allowedOrigin = '*';
98
+ if (typeof corsConfig.origin === 'string') {
99
+ allowedOrigin = corsConfig.origin;
100
+ } else if (Array.isArray(corsConfig.origin) && requestOrigin) {
101
+ if (corsConfig.origin.includes(requestOrigin)) {
102
+ allowedOrigin = requestOrigin;
103
+ }
104
+ }
105
+
106
+ const headers: Record<string, string> = {
107
+ 'Access-Control-Allow-Origin': allowedOrigin,
108
+ 'Access-Control-Allow-Methods': corsConfig.methods.join(', '),
109
+ 'Access-Control-Allow-Headers': corsConfig.headers.join(', '),
110
+ };
111
+
112
+ if (corsConfig.credentials) {
113
+ headers['Access-Control-Allow-Credentials'] = 'true';
114
+ }
115
+
116
+ return headers;
117
+ };
118
+
119
+ return {
120
+ async fetch(
121
+ request: Request,
122
+ env: E,
123
+ ctx: ExecutionContext,
124
+ ): Promise<Response> {
125
+ const url = new URL(request.url);
126
+ const corsHeaders = getCorsHeaders(request.headers.get('Origin'));
127
+
128
+ // Handle preflight
129
+ if (request.method === 'OPTIONS') {
130
+ return new Response(null, {
131
+ status: 204,
132
+ headers: {
133
+ ...corsHeaders,
134
+ 'Access-Control-Max-Age': '86400',
135
+ },
136
+ });
137
+ }
138
+
139
+ try {
140
+ // Custom onRequest handler
141
+ if (options.onRequest) {
142
+ const customResponse = await options.onRequest(request, env, ctx);
143
+ if (customResponse) {
144
+ return addCorsHeaders(customResponse, corsHeaders);
145
+ }
146
+ }
147
+
148
+ // API routes - /api/*
149
+ if (url.pathname.startsWith('/api/')) {
150
+ const response = await apiRouter.handle(request, env, ctx);
151
+ if (response) {
152
+ return addCorsHeaders(response, corsHeaders);
153
+ }
154
+ // No matching API route - fall through to 404
155
+ }
156
+
157
+ // Session routes - /session/*
158
+ if (url.pathname.startsWith('/session/')) {
159
+ return handleSessionRequest(
160
+ request,
161
+ env as unknown as BaseEnv,
162
+ corsHeaders,
163
+ );
164
+ }
165
+
166
+ // Routes registry - /routes
167
+ if (url.pathname.startsWith('/routes')) {
168
+ return handleRoutesRequest(
169
+ request,
170
+ env as unknown as BaseEnv,
171
+ corsHeaders,
172
+ );
173
+ }
174
+
175
+ // Health check
176
+ if (url.pathname === '/health') {
177
+ return new Response(
178
+ JSON.stringify({ status: 'ok', env: env.ENVIRONMENT }),
179
+ { headers: { ...corsHeaders, 'Content-Type': 'application/json' } },
180
+ );
181
+ }
182
+
183
+ // Custom 404 handler
184
+ if (options.notFound) {
185
+ const notFoundResponse = await options.notFound(request, env);
186
+ return addCorsHeaders(notFoundResponse, corsHeaders);
187
+ }
188
+
189
+ return new Response('Not found', { status: 404, headers: corsHeaders });
190
+ } catch (error) {
191
+ console.error('Worker error:', error);
192
+ return new Response(
193
+ JSON.stringify({
194
+ error: 'Internal server error',
195
+ message: error instanceof Error ? error.message : 'Unknown error',
196
+ }),
197
+ {
198
+ status: 500,
199
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
200
+ },
201
+ );
202
+ }
203
+ },
204
+ };
205
+ }
206
+
207
+ /** Add CORS headers to a response */
208
+ function addCorsHeaders(
209
+ response: Response,
210
+ corsHeaders: Record<string, string>,
211
+ ): Response {
212
+ const newHeaders = new Headers(response.headers);
213
+ for (const [key, value] of Object.entries(corsHeaders)) {
214
+ if (!newHeaders.has(key)) {
215
+ newHeaders.set(key, value);
216
+ }
217
+ }
218
+ return new Response(response.body, {
219
+ status: response.status,
220
+ statusText: response.statusText,
221
+ headers: newHeaders,
222
+ });
223
+ }
224
+
225
+ /** Cloudflare Worker exported handler interface */
226
+ interface ExportedHandler<E = unknown> {
227
+ fetch(request: Request, env: E, ctx: ExecutionContext): Promise<Response>;
228
+ }
229
+
230
+ // =============================================================================
231
+ // LEGACY DEFAULT EXPORT (for backwards compatibility)
232
+ // =============================================================================
233
+
234
+ interface BaseEnv {
235
+ PAGE_SESSIONS: DurableObjectNamespace;
236
+ ROUTES_REGISTRY: DurableObjectNamespace;
237
+ ENVIRONMENT?: string;
238
+ }
239
+
240
+ export default createAeonWorker();
241
+
242
+ /**
243
+ * Handle session requests - route to PageSession Durable Object
244
+ */
245
+ async function handleSessionRequest(
246
+ request: Request,
247
+ env: BaseEnv,
248
+ corsHeaders: Record<string, string>,
249
+ ): Promise<Response> {
250
+ const url = new URL(request.url);
251
+
252
+ // Extract session ID from path: /session/:sessionId/...
253
+ const pathParts = url.pathname.split('/').filter(Boolean);
254
+ const sessionId = pathParts[1];
255
+
256
+ if (!sessionId) {
257
+ return new Response('Session ID required', {
258
+ status: 400,
259
+ headers: corsHeaders,
260
+ });
261
+ }
262
+
263
+ // Get or create the Durable Object instance
264
+ const id = env.PAGE_SESSIONS.idFromName(sessionId);
265
+ const stub = env.PAGE_SESSIONS.get(id);
266
+
267
+ // Forward the request to the DO
268
+ const doUrl = new URL(request.url);
269
+ doUrl.pathname = '/' + pathParts.slice(2).join('/') || '/session';
270
+
271
+ const doRequest = new Request(doUrl.toString(), {
272
+ method: request.method,
273
+ headers: request.headers,
274
+ body: request.body,
275
+ });
276
+
277
+ const response = await stub.fetch(doRequest);
278
+
279
+ // Add CORS headers to response
280
+ const newHeaders = new Headers(response.headers);
281
+ Object.entries(corsHeaders).forEach(([key, value]) => {
282
+ newHeaders.set(key, value);
283
+ });
284
+
285
+ return new Response(response.body, {
286
+ status: response.status,
287
+ statusText: response.statusText,
288
+ headers: newHeaders,
289
+ });
290
+ }
291
+
292
+ /**
293
+ * Handle routes registry requests - route to singleton RoutesRegistry DO
294
+ */
295
+ async function handleRoutesRequest(
296
+ request: Request,
297
+ env: BaseEnv,
298
+ corsHeaders: Record<string, string>,
299
+ ): Promise<Response> {
300
+ // Use a singleton DO for routes registry
301
+ const id = env.ROUTES_REGISTRY.idFromName('__routes__');
302
+ const stub = env.ROUTES_REGISTRY.get(id);
303
+
304
+ const url = new URL(request.url);
305
+ const doUrl = new URL(request.url);
306
+ doUrl.pathname = url.pathname.replace('/routes', '') || '/routes';
307
+
308
+ const doRequest = new Request(doUrl.toString(), {
309
+ method: request.method,
310
+ headers: request.headers,
311
+ body: request.body,
312
+ });
313
+
314
+ const response = await stub.fetch(doRequest);
315
+
316
+ // Add CORS headers
317
+ const newHeaders = new Headers(response.headers);
318
+ Object.entries(corsHeaders).forEach(([key, value]) => {
319
+ newHeaders.set(key, value);
320
+ });
321
+
322
+ return new Response(response.body, {
323
+ status: response.status,
324
+ statusText: response.statusText,
325
+ headers: newHeaders,
326
+ });
327
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ESNext", "DOM", "WebWorker"],
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
+ "types": ["bun"]
17
+ },
18
+ "include": ["src/**/*"],
19
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
20
+ }