@affectively/aeon-pages-runtime 0.1.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.
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Speculation Manager
3
+ *
4
+ * Client-side prefetching and prerendering for instant navigation.
5
+ * Uses the Speculation Rules API when available, with fallback to link prefetch.
6
+ */
7
+ export interface SpeculationOptions {
8
+ /** Maximum paths to prefetch */
9
+ maxPrefetch?: number;
10
+ /** Maximum paths to prerender (more expensive) */
11
+ maxPrerender?: number;
12
+ /** Prefetch on hover delay (ms) */
13
+ hoverDelay?: number;
14
+ /** Prefetch on viewport intersection */
15
+ prefetchOnVisible?: boolean;
16
+ /** Intersection observer threshold */
17
+ visibilityThreshold?: number;
18
+ /** Cache duration for prefetched resources (ms) */
19
+ cacheDuration?: number;
20
+ /** Callback when speculation occurs */
21
+ onSpeculate?: (path: string, type: 'prefetch' | 'prerender') => void;
22
+ }
23
+ export interface SpeculationState {
24
+ prefetched: Set<string>;
25
+ prerendered: Set<string>;
26
+ pending: Set<string>;
27
+ }
28
+ declare function supportsSpeculationRules(): boolean;
29
+ declare function supportsLinkPrefetch(): boolean;
30
+ /**
31
+ * Add speculation rules to the document
32
+ */
33
+ declare function addSpeculationRules(prefetch: string[], prerender: string[]): HTMLScriptElement | null;
34
+ /**
35
+ * Prefetch a path using <link rel="prefetch">
36
+ */
37
+ declare function linkPrefetch(path: string): HTMLLinkElement | null;
38
+ export declare class SpeculationManager {
39
+ private options;
40
+ private state;
41
+ private observers;
42
+ private hoverTimers;
43
+ private speculationScript;
44
+ private prefetchLinks;
45
+ constructor(options?: SpeculationOptions);
46
+ /**
47
+ * Initialize speculation from server hints
48
+ */
49
+ initFromHints(prefetch?: string[], prerender?: string[]): void;
50
+ /**
51
+ * Prefetch a specific path
52
+ */
53
+ prefetch(path: string): boolean;
54
+ /**
55
+ * Watch an element for hover to prefetch its href
56
+ */
57
+ watchHover(element: HTMLAnchorElement): () => void;
58
+ /**
59
+ * Watch an element for visibility to prefetch its href
60
+ */
61
+ watchVisible(element: HTMLAnchorElement): () => void;
62
+ /**
63
+ * Auto-watch all internal links on the page
64
+ */
65
+ watchAllLinks(): () => void;
66
+ /**
67
+ * Clear all speculation state
68
+ */
69
+ clear(): void;
70
+ /**
71
+ * Get current speculation state
72
+ */
73
+ getState(): Readonly<SpeculationState>;
74
+ }
75
+ /**
76
+ * React hook for speculation management
77
+ * Usage: const speculation = useSpeculation({ maxPrefetch: 5 });
78
+ */
79
+ export declare function createSpeculationHook(useState: <T>(initial: T) => [T, (v: T) => void], useEffect: (effect: () => void | (() => void), deps: unknown[]) => void, useRef: <T>(initial: T) => {
80
+ current: T;
81
+ }): (options?: SpeculationOptions) => {
82
+ state: SpeculationState;
83
+ prefetch: (path: string) => boolean | undefined;
84
+ initFromHints: (prefetch: string[], prerender: string[]) => void | undefined;
85
+ clear: () => void | undefined;
86
+ };
87
+ /**
88
+ * Auto-initialize speculation from window.__AEON_SPECULATION__ hints
89
+ */
90
+ export declare function autoInitSpeculation(): SpeculationManager | null;
91
+ export { supportsSpeculationRules, supportsLinkPrefetch, addSpeculationRules, linkPrefetch, };
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Router Types for Aeon Flux
3
+ *
4
+ * Defines the core types for the personalized routing system.
5
+ */
6
+ export interface EmotionState {
7
+ /** Primary detected emotion */
8
+ primary: string;
9
+ /** Valence: negative (-1) to positive (1) */
10
+ valence: number;
11
+ /** Arousal: calm (0) to excited (1) */
12
+ arousal: number;
13
+ /** Confidence in detection (0-1) */
14
+ confidence: number;
15
+ /** Related emotions with intensity */
16
+ related?: Array<{
17
+ emotion: string;
18
+ intensity: number;
19
+ }>;
20
+ }
21
+ export interface Viewport {
22
+ width: number;
23
+ height: number;
24
+ devicePixelRatio?: number;
25
+ }
26
+ export type ConnectionType = 'slow-2g' | '2g' | '3g' | '4g' | 'fast';
27
+ export type UserTier = 'free' | 'starter' | 'pro' | 'enterprise';
28
+ export interface UserContext {
29
+ userId?: string;
30
+ tier: UserTier;
31
+ recentPages: string[];
32
+ dwellTimes: Map<string, number>;
33
+ clickPatterns: string[];
34
+ emotionState?: EmotionState;
35
+ preferences: Record<string, unknown>;
36
+ viewport: Viewport;
37
+ connection: ConnectionType;
38
+ reducedMotion: boolean;
39
+ localHour: number;
40
+ timezone: string;
41
+ sessionId?: string;
42
+ isNewSession: boolean;
43
+ sessionStartedAt?: Date;
44
+ }
45
+ export type ThemeMode = 'light' | 'dark' | 'auto';
46
+ export type LayoutDensity = 'compact' | 'normal' | 'comfortable';
47
+ export type LayoutType = 'dashboard' | 'chat' | 'settings' | 'tools' | 'marketing' | 'custom';
48
+ export interface SkeletonHints {
49
+ layout: LayoutType;
50
+ estimatedHeight: number;
51
+ sections?: Array<{
52
+ id: string;
53
+ height: number;
54
+ priority: number;
55
+ }>;
56
+ }
57
+ export interface RouteDecision {
58
+ route: string;
59
+ sessionId: string;
60
+ componentOrder?: string[];
61
+ hiddenComponents?: string[];
62
+ featureFlags?: Record<string, boolean>;
63
+ theme?: ThemeMode;
64
+ accent?: string;
65
+ density?: LayoutDensity;
66
+ prefetch?: string[];
67
+ prerender?: string[];
68
+ skeleton?: SkeletonHints;
69
+ routedAt: number;
70
+ routerName: string;
71
+ confidence: number;
72
+ }
73
+ export interface ComponentNode {
74
+ id: string;
75
+ type: string;
76
+ props?: Record<string, unknown>;
77
+ children?: string[];
78
+ requiredTier?: UserTier;
79
+ relevanceSignals?: string[];
80
+ defaultHidden?: boolean;
81
+ }
82
+ export interface ComponentTree {
83
+ rootId: string;
84
+ nodes: Map<string, ComponentNode>;
85
+ getNode(id: string): ComponentNode | undefined;
86
+ getChildren(id: string): ComponentNode[];
87
+ getSchema(): ComponentTreeSchema;
88
+ clone(): ComponentTree;
89
+ }
90
+ export interface ComponentTreeSchema {
91
+ rootId: string;
92
+ nodeCount: number;
93
+ nodeTypes: string[];
94
+ depth: number;
95
+ }
96
+ export interface RouterAdapter {
97
+ /** Adapter name for logging/debugging */
98
+ name: string;
99
+ /**
100
+ * Route a request with user context
101
+ * Returns personalized routing decision
102
+ */
103
+ route(path: string, context: UserContext, tree: ComponentTree): Promise<RouteDecision>;
104
+ /**
105
+ * Speculate likely next paths for prefetching
106
+ * Returns ordered list of probable next routes
107
+ */
108
+ speculate(currentPath: string, context: UserContext): Promise<string[]>;
109
+ /**
110
+ * Apply personalization to component tree
111
+ * Returns modified tree based on route decision
112
+ */
113
+ personalizeTree(tree: ComponentTree, decision: RouteDecision): ComponentTree;
114
+ /**
115
+ * Compute accent color from emotion state
116
+ */
117
+ emotionToAccent?(emotionState: EmotionState): string;
118
+ }
119
+ export interface AIRouterConfig {
120
+ endpoint: string;
121
+ timeout: number;
122
+ fallbackToHeuristic: boolean;
123
+ }
124
+ export interface SpeculationConfig {
125
+ enabled: boolean;
126
+ depth: number;
127
+ prerenderTop: number;
128
+ maxPrefetch: number;
129
+ }
130
+ export interface PersonalizationConfig {
131
+ featureGating: boolean;
132
+ emotionTheming: boolean;
133
+ componentOrdering: boolean;
134
+ densityAdaptation: boolean;
135
+ }
136
+ export interface RouterConfig {
137
+ adapter: 'heuristic' | 'ai' | 'hybrid' | RouterAdapter;
138
+ ai?: AIRouterConfig;
139
+ speculation?: SpeculationConfig;
140
+ personalization?: PersonalizationConfig;
141
+ }
142
+ export declare const DEFAULT_ROUTER_CONFIG: RouterConfig;
143
+ /**
144
+ * ESI - Edge Side Inference
145
+ *
146
+ * Like Varnish's ESI (Edge Side Includes) but for AI inference.
147
+ * Components can invoke edge inference at render time, embedding AI
148
+ * directly in templates.
149
+ *
150
+ * Example usage in templates:
151
+ * ```tsx
152
+ * <ESI.Infer model="llm" prompt="Greet the user warmly" />
153
+ * <ESI.Infer model="llm">Summarize: {content}</ESI.Infer>
154
+ * <ESI.Embed>Some text to embed</ESI.Embed>
155
+ * <ESI.Emotion>How is the user feeling?</ESI.Emotion>
156
+ * ```
157
+ */
158
+ export type ESIModel = 'llm' | 'embed' | 'vision' | 'tts' | 'stt' | 'emotion' | 'classify' | 'custom';
159
+ export type ESIContentType = 'text' | 'base64' | 'json' | 'template';
160
+ export interface ESIParams {
161
+ /** Model to use for inference */
162
+ model: ESIModel;
163
+ /** Specific model variant (e.g., 'gpt-4', 'mistral-7b') */
164
+ variant?: string;
165
+ /** Temperature for generation (0-2) */
166
+ temperature?: number;
167
+ /** Maximum tokens to generate */
168
+ maxTokens?: number;
169
+ /** Stop sequences */
170
+ stop?: string[];
171
+ /** Top-p sampling */
172
+ topP?: number;
173
+ /** Frequency penalty */
174
+ frequencyPenalty?: number;
175
+ /** Presence penalty */
176
+ presencePenalty?: number;
177
+ /** System prompt for context */
178
+ system?: string;
179
+ /** Enable streaming response */
180
+ stream?: boolean;
181
+ /** Cache duration in seconds (0 = no cache) */
182
+ cacheTtl?: number;
183
+ /** Cache key (auto-generated if not provided) */
184
+ cacheKey?: string;
185
+ /** Timeout in milliseconds */
186
+ timeout?: number;
187
+ /** Fallback content if inference fails */
188
+ fallback?: string;
189
+ /** Custom parameters for the model */
190
+ custom?: Record<string, unknown>;
191
+ }
192
+ export interface ESIContent {
193
+ /** Content type */
194
+ type: ESIContentType;
195
+ /** The actual content (prompt, base64 data, etc.) */
196
+ value: string;
197
+ /** Template variables for interpolation */
198
+ variables?: Record<string, unknown>;
199
+ }
200
+ export interface ESIDirective {
201
+ /** Unique ID for this ESI directive */
202
+ id: string;
203
+ /** Inference parameters */
204
+ params: ESIParams;
205
+ /** Content to process */
206
+ content: ESIContent;
207
+ /** Required user tier (for feature gating) */
208
+ requiredTier?: UserTier;
209
+ /** Context-aware: inject UserContext into prompt */
210
+ contextAware?: boolean;
211
+ /** Personalization signals to include */
212
+ signals?: Array<'emotion' | 'preferences' | 'history' | 'time' | 'device'>;
213
+ /** Cache key for this directive (auto-generated if not provided) */
214
+ cacheKey?: string;
215
+ }
216
+ export interface ESIResult {
217
+ /** Directive ID */
218
+ id: string;
219
+ /** Success status */
220
+ success: boolean;
221
+ /** Generated output */
222
+ output?: string;
223
+ /** For embeddings */
224
+ embedding?: number[];
225
+ /** For audio (TTS) */
226
+ audio?: ArrayBuffer;
227
+ /** Error message if failed */
228
+ error?: string;
229
+ /** Inference latency in ms */
230
+ latencyMs: number;
231
+ /** Was result from cache */
232
+ cached: boolean;
233
+ /** Model used */
234
+ model: string;
235
+ /** Tokens used (if applicable) */
236
+ tokens?: {
237
+ prompt: number;
238
+ completion: number;
239
+ total: number;
240
+ };
241
+ }
242
+ export interface ESIProcessor {
243
+ /** Processor name */
244
+ name: string;
245
+ /** Process a single ESI directive */
246
+ process(directive: ESIDirective, context: UserContext): Promise<ESIResult>;
247
+ /** Process multiple directives (batch optimization) */
248
+ processBatch(directives: ESIDirective[], context: UserContext): Promise<ESIResult[]>;
249
+ /** Stream inference result */
250
+ stream?(directive: ESIDirective, context: UserContext, onChunk: (chunk: string) => void): Promise<ESIResult>;
251
+ /** Warm up the processor (pre-load models, etc.) */
252
+ warmup?(): Promise<void>;
253
+ /** Check if a model is available */
254
+ isModelAvailable(model: ESIModel): boolean;
255
+ }
256
+ export interface ESIConfig {
257
+ /** Enable ESI processing */
258
+ enabled: boolean;
259
+ /** Edge inference endpoint */
260
+ endpoint: string;
261
+ /** Default timeout for inference */
262
+ timeout: number;
263
+ /** Default cache TTL */
264
+ defaultCacheTtl: number;
265
+ /** Maximum concurrent inferences */
266
+ maxConcurrent: number;
267
+ /** Models to pre-warm */
268
+ warmupModels?: ESIModel[];
269
+ /** Custom processor (plugin architecture) */
270
+ processor?: ESIProcessor;
271
+ /** Feature gating by tier */
272
+ tierLimits?: Record<UserTier, {
273
+ maxInferencesPerRequest: number;
274
+ allowedModels: ESIModel[];
275
+ maxTokens: number;
276
+ }>;
277
+ }
278
+ export declare const DEFAULT_ESI_CONFIG: ESIConfig;
279
+ export interface RouterConfigWithESI extends RouterConfig {
280
+ esi?: ESIConfig;
281
+ }
282
+ /** Presence info for a user/agent */
283
+ export interface PresenceUser {
284
+ /** User or agent ID */
285
+ userId: string;
286
+ /** User or agent role */
287
+ role: 'user' | 'assistant' | 'monitor' | 'admin';
288
+ /** Cursor position */
289
+ cursor?: {
290
+ x: number;
291
+ y: number;
292
+ };
293
+ /** Currently editing element path */
294
+ editingPath?: string;
295
+ /** User display name */
296
+ name?: string;
297
+ /** Avatar URL or initials */
298
+ avatar?: string;
299
+ /** Last active timestamp */
300
+ lastActive?: Date;
301
+ /** Is this a real-time presence (vs cached) */
302
+ isLive?: boolean;
303
+ /** User status (online, idle, etc.) */
304
+ status?: 'online' | 'idle' | 'away' | 'busy' | 'offline';
305
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Aeon Router - TypeScript wrapper for WASM router
3
+ *
4
+ * Handles route matching with Next.js-style patterns:
5
+ * - Static: /about
6
+ * - Dynamic: /blog/[slug]
7
+ * - Catch-all: /api/[...path]
8
+ * - Optional catch-all: /docs/[[...slug]]
9
+ */
10
+ import type { RouteDefinition, RouteMatch } from './types';
11
+ interface RouterOptions {
12
+ routesDir: string;
13
+ componentsDir?: string;
14
+ }
15
+ /**
16
+ * Aeon Router - file-based routing with dynamic segment support
17
+ */
18
+ export declare class AeonRouter {
19
+ private routes;
20
+ private routesDir;
21
+ private componentsDir?;
22
+ constructor(options: RouterOptions);
23
+ /**
24
+ * Scan the routes directory and build the route table
25
+ */
26
+ scan(): Promise<void>;
27
+ /**
28
+ * Reload routes (for hot reload)
29
+ */
30
+ reload(): Promise<void>;
31
+ /**
32
+ * Match a URL path to a route
33
+ */
34
+ match(path: string): RouteMatch | null;
35
+ /**
36
+ * Check if a route exists
37
+ */
38
+ hasRoute(path: string): boolean;
39
+ /**
40
+ * Get all registered routes
41
+ */
42
+ getRoutes(): RouteDefinition[];
43
+ /**
44
+ * Add a route manually (for dynamic creation)
45
+ */
46
+ addRoute(definition: RouteDefinition): void;
47
+ private scanDirectory;
48
+ private createRouteFromFile;
49
+ private parsePattern;
50
+ private matchSegments;
51
+ private generateSessionId;
52
+ private resolveSessionId;
53
+ private sortRoutes;
54
+ private routeSpecificity;
55
+ }
56
+ export {};
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Aeon Pages Bun Server
3
+ *
4
+ * Lightweight server for serving Aeon pages with:
5
+ * - Hot reload in development
6
+ * - Collaborative route mutations via Aeon sync
7
+ * - File system persistence
8
+ * - Personalized routing with speculation
9
+ */
10
+ import { AeonRouter } from './router';
11
+ import { AeonRouteRegistry } from './registry';
12
+ import type { AeonConfig } from './types';
13
+ import type { RouterConfig, RouteDecision, UserContext } from './router/types';
14
+ export interface ServerOptions {
15
+ config: AeonConfig;
16
+ /** Personalized router configuration */
17
+ router?: RouterConfig;
18
+ onRouteChange?: (route: string, type: 'add' | 'update' | 'remove') => void;
19
+ onRouteDecision?: (decision: RouteDecision, context: UserContext) => void;
20
+ }
21
+ /**
22
+ * Create an Aeon Pages server using Bun's native server
23
+ */
24
+ export declare function createAeonServer(options: ServerOptions): Promise<Bun.Server<undefined>>;
25
+ export { AeonRouter, AeonRouteRegistry };