@affectively/aeon-flux 0.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 (72) hide show
  1. package/README.md +438 -0
  2. package/examples/basic/aeon.config.ts +39 -0
  3. package/examples/basic/components/Cursor.tsx +88 -0
  4. package/examples/basic/components/OfflineIndicator.tsx +93 -0
  5. package/examples/basic/components/PresenceBar.tsx +68 -0
  6. package/examples/basic/package.json +20 -0
  7. package/examples/basic/pages/index.tsx +73 -0
  8. package/package.json +90 -0
  9. package/packages/benchmarks/src/benchmark.test.ts +644 -0
  10. package/packages/cli/package.json +43 -0
  11. package/packages/cli/src/commands/build.test.ts +649 -0
  12. package/packages/cli/src/commands/build.ts +853 -0
  13. package/packages/cli/src/commands/dev.ts +463 -0
  14. package/packages/cli/src/commands/init.ts +395 -0
  15. package/packages/cli/src/commands/start.ts +289 -0
  16. package/packages/cli/src/index.ts +102 -0
  17. package/packages/directives/src/use-aeon.ts +266 -0
  18. package/packages/react/package.json +34 -0
  19. package/packages/react/src/Link.tsx +355 -0
  20. package/packages/react/src/hooks/useAeonNavigation.ts +204 -0
  21. package/packages/react/src/hooks/usePilotNavigation.ts +253 -0
  22. package/packages/react/src/hooks/useServiceWorker.ts +276 -0
  23. package/packages/react/src/hooks.ts +192 -0
  24. package/packages/react/src/index.ts +89 -0
  25. package/packages/react/src/provider.tsx +428 -0
  26. package/packages/runtime/package.json +70 -0
  27. package/packages/runtime/schema.sql +40 -0
  28. package/packages/runtime/src/api-routes.ts +453 -0
  29. package/packages/runtime/src/benchmark.ts +145 -0
  30. package/packages/runtime/src/cache.ts +287 -0
  31. package/packages/runtime/src/durable-object.ts +847 -0
  32. package/packages/runtime/src/index.ts +235 -0
  33. package/packages/runtime/src/navigation.test.ts +432 -0
  34. package/packages/runtime/src/navigation.ts +412 -0
  35. package/packages/runtime/src/nextjs-adapter.ts +254 -0
  36. package/packages/runtime/src/predictor.ts +368 -0
  37. package/packages/runtime/src/registry.ts +339 -0
  38. package/packages/runtime/src/router/context-extractor.ts +394 -0
  39. package/packages/runtime/src/router/esi-control-react.tsx +1172 -0
  40. package/packages/runtime/src/router/esi-control.ts +488 -0
  41. package/packages/runtime/src/router/esi-react.tsx +600 -0
  42. package/packages/runtime/src/router/esi.ts +595 -0
  43. package/packages/runtime/src/router/heuristic-adapter.test.ts +272 -0
  44. package/packages/runtime/src/router/heuristic-adapter.ts +544 -0
  45. package/packages/runtime/src/router/index.ts +158 -0
  46. package/packages/runtime/src/router/speculation.ts +442 -0
  47. package/packages/runtime/src/router/types.ts +514 -0
  48. package/packages/runtime/src/router.test.ts +466 -0
  49. package/packages/runtime/src/router.ts +285 -0
  50. package/packages/runtime/src/server.ts +446 -0
  51. package/packages/runtime/src/service-worker.ts +418 -0
  52. package/packages/runtime/src/speculation.test.ts +360 -0
  53. package/packages/runtime/src/speculation.ts +456 -0
  54. package/packages/runtime/src/storage.test.ts +1201 -0
  55. package/packages/runtime/src/storage.ts +1031 -0
  56. package/packages/runtime/src/tree-compiler.ts +252 -0
  57. package/packages/runtime/src/types.ts +444 -0
  58. package/packages/runtime/src/worker.ts +300 -0
  59. package/packages/runtime/tsconfig.json +19 -0
  60. package/packages/runtime/wrangler.toml +41 -0
  61. package/packages/runtime-wasm/Cargo.lock +436 -0
  62. package/packages/runtime-wasm/Cargo.toml +29 -0
  63. package/packages/runtime-wasm/pkg/aeon_pages_runtime.d.ts +328 -0
  64. package/packages/runtime-wasm/pkg/aeon_pages_runtime.js +1267 -0
  65. package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm +0 -0
  66. package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm.d.ts +73 -0
  67. package/packages/runtime-wasm/pkg/package.json +21 -0
  68. package/packages/runtime-wasm/src/hydrate.rs +352 -0
  69. package/packages/runtime-wasm/src/lib.rs +189 -0
  70. package/packages/runtime-wasm/src/render.rs +629 -0
  71. package/packages/runtime-wasm/src/router.rs +298 -0
  72. package/rfcs/RFC-001-ZERO-DEPENDENCY-RENDERING.md +1446 -0
@@ -0,0 +1,488 @@
1
+ /**
2
+ * ESI Control Language
3
+ *
4
+ * Adds conditional display and structured output validation to ESI.
5
+ * Uses Zod for schema validation to ensure trusted, typed results.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * // Conditional display based on inference
10
+ * <ESI.If
11
+ * prompt="Is this user likely to churn?"
12
+ * schema={z.object({ likelihood: z.number(), reason: z.string() })}
13
+ * when={(result) => result.likelihood > 0.7}
14
+ * >
15
+ * <RetentionOffer />
16
+ * </ESI.If>
17
+ *
18
+ * // Structured output with Zod
19
+ * <ESI.Infer
20
+ * schema={z.object({
21
+ * sentiment: z.enum(['positive', 'negative', 'neutral']),
22
+ * confidence: z.number(),
23
+ * topics: z.array(z.string()),
24
+ * })}
25
+ * >
26
+ * Analyze this text: {userInput}
27
+ * </ESI.Infer>
28
+ *
29
+ * // Switch/match on inference result
30
+ * <ESI.Match
31
+ * prompt="Classify user intent"
32
+ * schema={z.object({ intent: z.enum(['browse', 'buy', 'support', 'leave']) })}
33
+ * >
34
+ * <ESI.Case match={(r) => r.intent === 'buy'}><CheckoutCTA /></ESI.Case>
35
+ * <ESI.Case match={(r) => r.intent === 'support'}><HelpWidget /></ESI.Case>
36
+ * <ESI.Default><BrowsePrompt /></ESI.Default>
37
+ * </ESI.Match>
38
+ * ```
39
+ */
40
+
41
+ import type { ZodType, ZodTypeDef, z } from 'zod';
42
+ import type {
43
+ ESIDirective,
44
+ ESIParams,
45
+ ESIResult,
46
+ UserContext,
47
+ } from './types';
48
+
49
+ // ============================================================================
50
+ // Schema Types
51
+ // ============================================================================
52
+
53
+ /**
54
+ * ESI with Zod schema for validated, typed output
55
+ */
56
+ export interface ESISchemaParams<T> extends Omit<ESIParams, 'model'> {
57
+ /** Zod schema for output validation */
58
+ schema: ZodType<T, ZodTypeDef, unknown>;
59
+
60
+ /** Model defaults to 'llm' for structured output */
61
+ model?: 'llm';
62
+
63
+ /** Retry on validation failure */
64
+ retryOnValidationError?: boolean;
65
+
66
+ /** Max retries */
67
+ maxRetries?: number;
68
+ }
69
+
70
+ export interface ESISchemaResult<T> extends Omit<ESIResult, 'output'> {
71
+ /** Validated, typed output */
72
+ data?: T;
73
+
74
+ /** Raw output before validation */
75
+ rawOutput?: string;
76
+
77
+ /** Validation errors if any */
78
+ validationErrors?: string[];
79
+ }
80
+
81
+ // ============================================================================
82
+ // Control Flow Types
83
+ // ============================================================================
84
+
85
+ /**
86
+ * Condition function type
87
+ */
88
+ export type ESICondition<T> = (result: T, context: UserContext) => boolean;
89
+
90
+ /**
91
+ * ESI.If directive - conditional rendering based on inference
92
+ */
93
+ export interface ESIIfDirective<T> {
94
+ /** Unique ID */
95
+ id: string;
96
+
97
+ /** Prompt to evaluate */
98
+ prompt: string;
99
+
100
+ /** Schema for structured output */
101
+ schema: ZodType<T, ZodTypeDef, unknown>;
102
+
103
+ /** Condition to check */
104
+ when: ESICondition<T>;
105
+
106
+ /** Inference params */
107
+ params?: Partial<ESIParams>;
108
+
109
+ /** Cache the condition result */
110
+ cacheCondition?: boolean;
111
+ }
112
+
113
+ /**
114
+ * ESI.Match directive - switch/case based on inference
115
+ */
116
+ export interface ESIMatchDirective<T> {
117
+ /** Unique ID */
118
+ id: string;
119
+
120
+ /** Prompt to evaluate */
121
+ prompt: string;
122
+
123
+ /** Schema for structured output */
124
+ schema: ZodType<T, ZodTypeDef, unknown>;
125
+
126
+ /** Cases to match */
127
+ cases: Array<{
128
+ match: ESICondition<T>;
129
+ id: string;
130
+ }>;
131
+
132
+ /** Default case ID if no match */
133
+ defaultCase?: string;
134
+
135
+ /** Inference params */
136
+ params?: Partial<ESIParams>;
137
+ }
138
+
139
+ /**
140
+ * Result of control flow evaluation
141
+ */
142
+ export interface ESIControlResult<T> {
143
+ /** The directive ID */
144
+ id: string;
145
+
146
+ /** Whether condition was met (for If) */
147
+ conditionMet?: boolean;
148
+
149
+ /** Matched case ID (for Match) */
150
+ matchedCase?: string;
151
+
152
+ /** The validated data */
153
+ data?: T;
154
+
155
+ /** Inference result */
156
+ inferenceResult: ESISchemaResult<T>;
157
+ }
158
+
159
+ // ============================================================================
160
+ // Schema Validation
161
+ // ============================================================================
162
+
163
+ /**
164
+ * Generate a JSON schema prompt suffix for structured output
165
+ */
166
+ export function generateSchemaPrompt<T>(schema: ZodType<T, ZodTypeDef, unknown>): string {
167
+ // Extract schema description for the prompt
168
+ // This helps the LLM understand the expected output format
169
+ const schemaDescription = describeZodSchema(schema);
170
+
171
+ return `
172
+
173
+ Respond with valid JSON matching this schema:
174
+ ${schemaDescription}
175
+
176
+ Output ONLY the JSON, no markdown, no explanation.`;
177
+ }
178
+
179
+ /**
180
+ * Describe a Zod schema in a way the LLM can understand
181
+ */
182
+ function describeZodSchema<T>(schema: ZodType<T, ZodTypeDef, unknown>): string {
183
+ // Get the schema's shape if it's an object
184
+ const def = schema._def as Record<string, unknown>;
185
+
186
+ if (def.typeName === 'ZodObject') {
187
+ const shape = def.shape as Record<string, ZodType<unknown, ZodTypeDef, unknown>>;
188
+ const fields = Object.entries(shape).map(([key, fieldSchema]) => {
189
+ const fieldDef = fieldSchema._def as Record<string, unknown>;
190
+ return ` "${key}": ${describeZodType(fieldDef)}`;
191
+ });
192
+ return `{\n${fields.join(',\n')}\n}`;
193
+ }
194
+
195
+ return describeZodType(def);
196
+ }
197
+
198
+ /**
199
+ * Describe a Zod type
200
+ */
201
+ function describeZodType(def: Record<string, unknown>): string {
202
+ const typeName = def.typeName as string;
203
+
204
+ switch (typeName) {
205
+ case 'ZodString':
206
+ return 'string';
207
+ case 'ZodNumber':
208
+ return 'number';
209
+ case 'ZodBoolean':
210
+ return 'boolean';
211
+ case 'ZodArray':
212
+ const innerType = def.type as ZodType<unknown, ZodTypeDef, unknown>;
213
+ return `array of ${describeZodType(innerType._def as Record<string, unknown>)}`;
214
+ case 'ZodEnum':
215
+ const values = def.values as string[];
216
+ return `one of: ${values.map((v) => `"${v}"`).join(' | ')}`;
217
+ case 'ZodLiteral':
218
+ return JSON.stringify(def.value);
219
+ case 'ZodOptional':
220
+ const optionalType = def.innerType as ZodType<unknown, ZodTypeDef, unknown>;
221
+ return `${describeZodType(optionalType._def as Record<string, unknown>)} (optional)`;
222
+ case 'ZodNullable':
223
+ const nullableType = def.innerType as ZodType<unknown, ZodTypeDef, unknown>;
224
+ return `${describeZodType(nullableType._def as Record<string, unknown>)} or null`;
225
+ case 'ZodObject':
226
+ return 'object';
227
+ default:
228
+ return 'any';
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Parse and validate LLM output against a Zod schema
234
+ */
235
+ export function parseWithSchema<T>(
236
+ output: string,
237
+ schema: ZodType<T, ZodTypeDef, unknown>
238
+ ): { success: true; data: T } | { success: false; errors: string[] } {
239
+ // Try to extract JSON from the output
240
+ let jsonStr = output.trim();
241
+
242
+ // Handle markdown code blocks
243
+ if (jsonStr.startsWith('```')) {
244
+ const match = jsonStr.match(/```(?:json)?\s*([\s\S]*?)```/);
245
+ if (match) {
246
+ jsonStr = match[1].trim();
247
+ }
248
+ }
249
+
250
+ // Try to parse JSON
251
+ let parsed: unknown;
252
+ try {
253
+ parsed = JSON.parse(jsonStr);
254
+ } catch (e) {
255
+ // Try to find JSON object in the output
256
+ const jsonMatch = jsonStr.match(/\{[\s\S]*\}/);
257
+ if (jsonMatch) {
258
+ try {
259
+ parsed = JSON.parse(jsonMatch[0]);
260
+ } catch {
261
+ return {
262
+ success: false,
263
+ errors: [`Failed to parse JSON: ${e instanceof Error ? e.message : 'Unknown error'}`],
264
+ };
265
+ }
266
+ } else {
267
+ return {
268
+ success: false,
269
+ errors: [`No valid JSON found in output`],
270
+ };
271
+ }
272
+ }
273
+
274
+ // Validate against schema
275
+ const result = schema.safeParse(parsed);
276
+
277
+ if (result.success) {
278
+ return { success: true, data: result.data };
279
+ }
280
+
281
+ return {
282
+ success: false,
283
+ errors: result.error.errors.map(
284
+ (e) => `${e.path.join('.')}: ${e.message}`
285
+ ),
286
+ };
287
+ }
288
+
289
+ // ============================================================================
290
+ // Control Flow Processor
291
+ // ============================================================================
292
+
293
+ export interface ESIControlProcessor {
294
+ /**
295
+ * Process an ESI.If directive
296
+ */
297
+ processIf<T>(
298
+ directive: ESIIfDirective<T>,
299
+ context: UserContext
300
+ ): Promise<ESIControlResult<T>>;
301
+
302
+ /**
303
+ * Process an ESI.Match directive
304
+ */
305
+ processMatch<T>(
306
+ directive: ESIMatchDirective<T>,
307
+ context: UserContext
308
+ ): Promise<ESIControlResult<T>>;
309
+
310
+ /**
311
+ * Process an ESI directive with schema validation
312
+ */
313
+ processWithSchema<T>(
314
+ prompt: string,
315
+ schema: ZodType<T, ZodTypeDef, unknown>,
316
+ params: Partial<ESIParams>,
317
+ context: UserContext
318
+ ): Promise<ESISchemaResult<T>>;
319
+ }
320
+
321
+ /**
322
+ * Create a control processor that wraps an ESI processor
323
+ */
324
+ export function createControlProcessor(
325
+ processESI: (directive: ESIDirective, context: UserContext) => Promise<ESIResult>
326
+ ): ESIControlProcessor {
327
+ return {
328
+ async processWithSchema<T>(
329
+ prompt: string,
330
+ schema: ZodType<T, ZodTypeDef, unknown>,
331
+ params: Partial<ESIParams>,
332
+ context: UserContext
333
+ ): Promise<ESISchemaResult<T>> {
334
+ // Add schema prompt
335
+ const fullPrompt = prompt + generateSchemaPrompt(schema);
336
+
337
+ const directive: ESIDirective = {
338
+ id: `esi-schema-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
339
+ params: {
340
+ model: 'llm',
341
+ ...params,
342
+ },
343
+ content: {
344
+ type: 'text',
345
+ value: fullPrompt,
346
+ },
347
+ };
348
+
349
+ const result = await processESI(directive, context);
350
+
351
+ if (!result.success || !result.output) {
352
+ return {
353
+ ...result,
354
+ validationErrors: result.error ? [result.error] : ['No output'],
355
+ };
356
+ }
357
+
358
+ const parseResult = parseWithSchema(result.output, schema);
359
+
360
+ if (parseResult.success) {
361
+ return {
362
+ ...result,
363
+ data: parseResult.data,
364
+ rawOutput: result.output,
365
+ };
366
+ }
367
+
368
+ // Retry logic could go here
369
+ return {
370
+ ...result,
371
+ rawOutput: result.output,
372
+ validationErrors: parseResult.errors,
373
+ };
374
+ },
375
+
376
+ async processIf<T>(
377
+ directive: ESIIfDirective<T>,
378
+ context: UserContext
379
+ ): Promise<ESIControlResult<T>> {
380
+ const schemaResult = await this.processWithSchema(
381
+ directive.prompt,
382
+ directive.schema,
383
+ directive.params || {},
384
+ context
385
+ );
386
+
387
+ let conditionMet = false;
388
+
389
+ if (schemaResult.data !== undefined) {
390
+ try {
391
+ conditionMet = directive.when(schemaResult.data, context);
392
+ } catch (e) {
393
+ // Condition evaluation failed
394
+ conditionMet = false;
395
+ }
396
+ }
397
+
398
+ return {
399
+ id: directive.id,
400
+ conditionMet,
401
+ data: schemaResult.data,
402
+ inferenceResult: schemaResult,
403
+ };
404
+ },
405
+
406
+ async processMatch<T>(
407
+ directive: ESIMatchDirective<T>,
408
+ context: UserContext
409
+ ): Promise<ESIControlResult<T>> {
410
+ const schemaResult = await this.processWithSchema(
411
+ directive.prompt,
412
+ directive.schema,
413
+ directive.params || {},
414
+ context
415
+ );
416
+
417
+ let matchedCase: string | undefined;
418
+
419
+ if (schemaResult.data !== undefined) {
420
+ for (const caseItem of directive.cases) {
421
+ try {
422
+ if (caseItem.match(schemaResult.data, context)) {
423
+ matchedCase = caseItem.id;
424
+ break;
425
+ }
426
+ } catch {
427
+ // Continue to next case
428
+ }
429
+ }
430
+
431
+ // Use default if no match
432
+ if (!matchedCase && directive.defaultCase) {
433
+ matchedCase = directive.defaultCase;
434
+ }
435
+ }
436
+
437
+ return {
438
+ id: directive.id,
439
+ matchedCase,
440
+ data: schemaResult.data,
441
+ inferenceResult: schemaResult,
442
+ };
443
+ },
444
+ };
445
+ }
446
+
447
+ // ============================================================================
448
+ // Directive Builders
449
+ // ============================================================================
450
+
451
+ /**
452
+ * Create an ESI.If directive
453
+ */
454
+ export function esiIf<T>(
455
+ prompt: string,
456
+ schema: ZodType<T, ZodTypeDef, unknown>,
457
+ when: ESICondition<T>,
458
+ options: Partial<ESIParams> = {}
459
+ ): ESIIfDirective<T> {
460
+ return {
461
+ id: `esi-if-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
462
+ prompt,
463
+ schema,
464
+ when,
465
+ params: options,
466
+ };
467
+ }
468
+
469
+ /**
470
+ * Create an ESI.Match directive
471
+ */
472
+ export function esiMatch<T>(
473
+ prompt: string,
474
+ schema: ZodType<T, ZodTypeDef, unknown>,
475
+ cases: Array<{ match: ESICondition<T>; id: string }>,
476
+ defaultCase?: string,
477
+ options: Partial<ESIParams> = {}
478
+ ): ESIMatchDirective<T> {
479
+ return {
480
+ id: `esi-match-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
481
+ prompt,
482
+ schema,
483
+ cases,
484
+ defaultCase,
485
+ params: options,
486
+ };
487
+ }
488
+