@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,399 @@
|
|
|
1
|
+
# @affectively/aeon-pages-runtime
|
|
2
|
+
|
|
3
|
+
Edge-first rendering runtime for Aeon-Flux with Edge Side Inference (ESI), hyperpersonalized routing, and real-time collaboration.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @affectively/aeon-pages-runtime
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
### Edge Side Inference (ESI)
|
|
14
|
+
|
|
15
|
+
AI-powered personalization at render time. Components that adapt to user context before reaching the browser.
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { ESI, ESIProvider } from '@affectively/aeon-pages-runtime/router';
|
|
19
|
+
|
|
20
|
+
function App() {
|
|
21
|
+
return (
|
|
22
|
+
<ESIProvider>
|
|
23
|
+
{/* AI-generated personalized greeting */}
|
|
24
|
+
<ESI.Infer prompt="Greet the user based on time of day">
|
|
25
|
+
{(greeting) => <h1>{greeting}</h1>}
|
|
26
|
+
</ESI.Infer>
|
|
27
|
+
|
|
28
|
+
{/* Tier-gated content */}
|
|
29
|
+
<ESI.TierGate tier="premium">
|
|
30
|
+
<PremiumFeatures />
|
|
31
|
+
</ESI.TierGate>
|
|
32
|
+
|
|
33
|
+
{/* Emotion-aware content */}
|
|
34
|
+
<ESI.EmotionGate emotions={['calm', 'content']}>
|
|
35
|
+
<RelaxingContent />
|
|
36
|
+
</ESI.EmotionGate>
|
|
37
|
+
</ESIProvider>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### AI Translation
|
|
43
|
+
|
|
44
|
+
Automatic translation at the edge. Supports 33 languages with LLM-based translation.
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { ESI, TranslationProvider } from '@affectively/aeon-pages-runtime/router';
|
|
48
|
+
|
|
49
|
+
// App-wide translation with user's preferred language
|
|
50
|
+
function App() {
|
|
51
|
+
return (
|
|
52
|
+
<TranslationProvider defaultLanguage="es">
|
|
53
|
+
<ESI.Translate>Welcome to our platform</ESI.Translate>
|
|
54
|
+
{/* Renders: "Bienvenido a nuestra plataforma" */}
|
|
55
|
+
</TranslationProvider>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Explicit target language
|
|
60
|
+
<ESI.Translate targetLanguage="ja">Hello world</ESI.Translate>
|
|
61
|
+
// Renders: "こんにちは世界"
|
|
62
|
+
|
|
63
|
+
// With context for better translation quality
|
|
64
|
+
<ESI.Translate
|
|
65
|
+
targetLanguage="fr"
|
|
66
|
+
context="emotional wellness app"
|
|
67
|
+
>
|
|
68
|
+
We're here to help you understand your feelings.
|
|
69
|
+
</ESI.Translate>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### Data-Attribute Translation
|
|
73
|
+
|
|
74
|
+
Automatically translate any element with `data-translate`:
|
|
75
|
+
|
|
76
|
+
```html
|
|
77
|
+
<p data-translate data-target-lang="es">Hello world</p>
|
|
78
|
+
<!-- Becomes: "Hola mundo" -->
|
|
79
|
+
|
|
80
|
+
<span data-translate data-translate-context="formal, business">
|
|
81
|
+
Please review the attached document.
|
|
82
|
+
</span>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { useTranslationObserver } from '@affectively/aeon-pages-runtime/router';
|
|
87
|
+
|
|
88
|
+
function App() {
|
|
89
|
+
// Automatically translates all data-translate elements
|
|
90
|
+
useTranslationObserver({ defaultTargetLanguage: 'es' });
|
|
91
|
+
|
|
92
|
+
return <div data-translate>Hello</div>;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Language Configuration Priority
|
|
97
|
+
|
|
98
|
+
1. `ESI.Translate` `targetLanguage` prop
|
|
99
|
+
2. `TranslationProvider` context
|
|
100
|
+
3. `window.__AEON_ESI_STATE__.preferences.language`
|
|
101
|
+
4. `<meta name="aeon-language" content="es">`
|
|
102
|
+
5. User preferences
|
|
103
|
+
6. Browser `navigator.language`
|
|
104
|
+
|
|
105
|
+
#### Supported Languages
|
|
106
|
+
|
|
107
|
+
`en`, `es`, `fr`, `de`, `it`, `pt`, `nl`, `pl`, `ru`, `zh`, `ja`, `ko`, `ar`, `hi`, `bn`, `vi`, `th`, `tr`, `id`, `ms`, `tl`, `sv`, `da`, `no`, `fi`, `cs`, `el`, `he`, `uk`, `ro`, `hu`, `ca`, `hy`
|
|
108
|
+
|
|
109
|
+
### Hyperpersonalized Routing
|
|
110
|
+
|
|
111
|
+
Routes adapt to user context: time, location, emotion state, and tier.
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import { HeuristicAdapter } from '@affectively/aeon-pages-runtime/router';
|
|
115
|
+
|
|
116
|
+
const adapter = new HeuristicAdapter({
|
|
117
|
+
tierFeatures: {
|
|
118
|
+
free: { maxAIInferences: 5 },
|
|
119
|
+
premium: { maxAIInferences: 100 },
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Route decision based on user context
|
|
124
|
+
const decision = await adapter.decide(userContext, '/dashboard');
|
|
125
|
+
// Returns: layout, theme, skeleton hints, prefetch suggestions
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Speculative Pre-rendering
|
|
129
|
+
|
|
130
|
+
Pages render before you click. Zero perceived latency.
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { SpeculationManager, autoInitSpeculation } from '@affectively/aeon-pages-runtime/router';
|
|
134
|
+
|
|
135
|
+
// Auto-initialize based on browser support
|
|
136
|
+
autoInitSpeculation();
|
|
137
|
+
|
|
138
|
+
// Or manual control
|
|
139
|
+
const speculation = new SpeculationManager({
|
|
140
|
+
prefetchUrls: ['/dashboard', '/settings'],
|
|
141
|
+
prerenderUrls: ['/about'],
|
|
142
|
+
});
|
|
143
|
+
speculation.start();
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### ESI Control Components
|
|
147
|
+
|
|
148
|
+
Declarative control flow with AI-powered conditions.
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { ESI } from '@affectively/aeon-pages-runtime/router';
|
|
152
|
+
|
|
153
|
+
// Conditional rendering
|
|
154
|
+
<ESI.If condition="user.tier === 'premium'">
|
|
155
|
+
<PremiumBadge />
|
|
156
|
+
</ESI.If>
|
|
157
|
+
|
|
158
|
+
// Pattern matching
|
|
159
|
+
<ESI.Match value={userEmotion}>
|
|
160
|
+
<ESI.Case when="happy"><HappyContent /></ESI.Case>
|
|
161
|
+
<ESI.Case when="sad"><SupportContent /></ESI.Case>
|
|
162
|
+
<ESI.Default><NeutralContent /></ESI.Default>
|
|
163
|
+
</ESI.Match>
|
|
164
|
+
|
|
165
|
+
// Time-based gates
|
|
166
|
+
<ESI.TimeGate hours={{ start: 9, end: 17 }}>
|
|
167
|
+
<BusinessHoursContent />
|
|
168
|
+
</ESI.TimeGate>
|
|
169
|
+
|
|
170
|
+
// A/B Testing
|
|
171
|
+
<ESI.ABTest variants={['control', 'variant-a', 'variant-b']}>
|
|
172
|
+
{(variant) => <Component variant={variant} />}
|
|
173
|
+
</ESI.ABTest>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Cyrano Whisper Channel
|
|
177
|
+
|
|
178
|
+
AI assistant guidance with contextual awareness.
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { esiCyrano, esiHalo } from '@affectively/aeon-pages-runtime/router';
|
|
182
|
+
|
|
183
|
+
// Generate AI whisper for user guidance
|
|
184
|
+
const whisper = esiCyrano({
|
|
185
|
+
intent: 'guide',
|
|
186
|
+
tone: 'warm',
|
|
187
|
+
context: sessionContext,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Halo insights for behavioral patterns
|
|
191
|
+
const insight = esiHalo({
|
|
192
|
+
observation: 'user-hesitation',
|
|
193
|
+
action: 'offer-help',
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Merkle-Based UCAN Capabilities
|
|
198
|
+
|
|
199
|
+
Fine-grained access control to component nodes using Merkle hashes. Integrates with UCAN tokens for cryptographic authorization.
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
import {
|
|
203
|
+
createNodeCapabilityVerifier,
|
|
204
|
+
createNodeReadCapability,
|
|
205
|
+
createTreeCapability,
|
|
206
|
+
checkNodeAccess,
|
|
207
|
+
} from '@affectively/aeon-pages-runtime';
|
|
208
|
+
|
|
209
|
+
// Create capabilities for specific nodes by Merkle hash
|
|
210
|
+
const readCapability = createNodeReadCapability('a1b2c3d4e5f6');
|
|
211
|
+
// { can: 'aeon:node:read', with: 'merkle:a1b2c3d4e5f6' }
|
|
212
|
+
|
|
213
|
+
// Create tree capability (grants access to node and all descendants)
|
|
214
|
+
const treeCapability = createTreeCapability('a1b2c3d4e5f6');
|
|
215
|
+
// { can: 'aeon:node:*', with: 'tree:a1b2c3d4e5f6' }
|
|
216
|
+
|
|
217
|
+
// Create path-based capability (grants access to all nodes on a route)
|
|
218
|
+
const pathCapability = createPathCapability('/dashboard/*');
|
|
219
|
+
// { can: 'aeon:node:*', with: 'path:/dashboard/*' }
|
|
220
|
+
|
|
221
|
+
// Verify access to a specific node
|
|
222
|
+
const canAccess = checkNodeAccess(
|
|
223
|
+
userCapabilities,
|
|
224
|
+
{ merkleHash: 'a1b2c3d4e5f6', routePath: '/dashboard' },
|
|
225
|
+
'read'
|
|
226
|
+
);
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### Resource Formats
|
|
230
|
+
|
|
231
|
+
| Format | Example | Description |
|
|
232
|
+
|--------|---------|-------------|
|
|
233
|
+
| `merkle:<hash>` | `merkle:a1b2c3d4e5f6` | Exact match on Merkle hash |
|
|
234
|
+
| `tree:<hash>` | `tree:a1b2c3d4e5f6` | Match node or any ancestor |
|
|
235
|
+
| `path:<route>` | `path:/dashboard/*` | All nodes on a route (wildcards) |
|
|
236
|
+
| `*` | `*` | Match any node (wildcard) |
|
|
237
|
+
|
|
238
|
+
#### Capability Actions
|
|
239
|
+
|
|
240
|
+
| Action | Description |
|
|
241
|
+
|--------|-------------|
|
|
242
|
+
| `aeon:node:read` | Read access to node |
|
|
243
|
+
| `aeon:node:write` | Write access to node |
|
|
244
|
+
| `aeon:node:*` | Full access to node |
|
|
245
|
+
|
|
246
|
+
#### Creating a Verifier from UCAN Token
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
import { createNodeCapabilityVerifier } from '@affectively/aeon-pages-runtime';
|
|
250
|
+
|
|
251
|
+
const verifier = createNodeCapabilityVerifier(token, {
|
|
252
|
+
extractCapabilities: async (t) => {
|
|
253
|
+
const decoded = await decodeUCAN(t);
|
|
254
|
+
return decoded.capabilities;
|
|
255
|
+
},
|
|
256
|
+
verifyToken: async (t) => {
|
|
257
|
+
return await verifyUCANSignature(t);
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Check if user can read a specific node
|
|
262
|
+
const canRead = await verifier(
|
|
263
|
+
{ merkleHash: 'a1b2c3d4e5f6' },
|
|
264
|
+
'read'
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
// Check with full tree path context
|
|
268
|
+
const canWrite = await verifier(
|
|
269
|
+
{
|
|
270
|
+
merkleHash: 'a1b2c3d4e5f6',
|
|
271
|
+
treePath: ['root', 'layout', 'header', 'button'],
|
|
272
|
+
ancestorHashes: ['f1e2d3c4b5a6', 'b2c3d4e5f6a1'],
|
|
273
|
+
routePath: '/dashboard',
|
|
274
|
+
},
|
|
275
|
+
'write'
|
|
276
|
+
);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### Integration with Analytics
|
|
280
|
+
|
|
281
|
+
Merkle capabilities integrate seamlessly with `@affectively/aeon-pages-analytics`:
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
import { buildMerkleTreeSync } from '@affectively/aeon-pages-analytics';
|
|
285
|
+
import { checkNodeAccess } from '@affectively/aeon-pages-runtime';
|
|
286
|
+
|
|
287
|
+
// Build Merkle tree from component tree
|
|
288
|
+
const merkleTree = buildMerkleTreeSync(componentTree);
|
|
289
|
+
|
|
290
|
+
// Check access for each node
|
|
291
|
+
for (const [nodeId, merkleNode] of merkleTree) {
|
|
292
|
+
const canEdit = checkNodeAccess(
|
|
293
|
+
userCapabilities,
|
|
294
|
+
{
|
|
295
|
+
merkleHash: merkleNode.hash,
|
|
296
|
+
treePath: merkleNode.path,
|
|
297
|
+
routePath: currentRoute,
|
|
298
|
+
},
|
|
299
|
+
'write'
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
if (canEdit) {
|
|
303
|
+
// Show edit controls for this node
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## API Reference
|
|
309
|
+
|
|
310
|
+
### Components
|
|
311
|
+
|
|
312
|
+
| Component | Description |
|
|
313
|
+
|-----------|-------------|
|
|
314
|
+
| `ESI.Provider` | Root ESI context provider |
|
|
315
|
+
| `ESI.Infer` | AI inference at render time |
|
|
316
|
+
| `ESI.Translate` | Automatic translation |
|
|
317
|
+
| `ESI.TranslationProvider` | App-wide translation context |
|
|
318
|
+
| `ESI.TierGate` | Tier-based content gating |
|
|
319
|
+
| `ESI.EmotionGate` | Emotion-based content gating |
|
|
320
|
+
| `ESI.TimeGate` | Time-based content gating |
|
|
321
|
+
| `ESI.If` / `ESI.Show` / `ESI.Hide` | Conditional rendering |
|
|
322
|
+
| `ESI.Match` / `ESI.Case` | Pattern matching |
|
|
323
|
+
| `ESI.ABTest` | A/B testing |
|
|
324
|
+
| `ESI.ForEach` | AI-powered iteration |
|
|
325
|
+
| `ESI.Select` | AI-powered selection |
|
|
326
|
+
|
|
327
|
+
### Hooks
|
|
328
|
+
|
|
329
|
+
| Hook | Description |
|
|
330
|
+
|------|-------------|
|
|
331
|
+
| `useESI()` | Access ESI context and processing |
|
|
332
|
+
| `useESIInfer()` | AI inference hook |
|
|
333
|
+
| `useTranslation()` | Translation context access |
|
|
334
|
+
| `useTranslationObserver()` | Auto-translate data-attribute elements |
|
|
335
|
+
| `useGlobalESIState()` | Access global ESI state |
|
|
336
|
+
| `useESITier()` | Get current user tier |
|
|
337
|
+
| `useESIEmotionState()` | Get current emotion state |
|
|
338
|
+
|
|
339
|
+
### Functions
|
|
340
|
+
|
|
341
|
+
| Function | Description |
|
|
342
|
+
|----------|-------------|
|
|
343
|
+
| `esiTranslate()` | Create translation directive |
|
|
344
|
+
| `translateWithAIGateway()` | Direct AI Gateway translation |
|
|
345
|
+
| `normalizeLanguageCode()` | Normalize language code |
|
|
346
|
+
| `getSupportedLanguages()` | Get list of supported languages |
|
|
347
|
+
| `initTranslationObserver()` | Auto-init DOM translation observer |
|
|
348
|
+
|
|
349
|
+
### Merkle Capability Functions
|
|
350
|
+
|
|
351
|
+
| Function | Description |
|
|
352
|
+
|----------|-------------|
|
|
353
|
+
| `createNodeReadCapability(hash)` | Create read capability for a Merkle hash |
|
|
354
|
+
| `createNodeWriteCapability(hash)` | Create write capability for a Merkle hash |
|
|
355
|
+
| `createTreeCapability(hash, action)` | Create tree capability (node + descendants) |
|
|
356
|
+
| `createPathCapability(path, action)` | Create path-based capability |
|
|
357
|
+
| `createWildcardNodeCapability(action)` | Create wildcard capability (all nodes) |
|
|
358
|
+
| `createNodeCapabilityVerifier(token, opts)` | Create verifier from UCAN token |
|
|
359
|
+
| `checkNodeAccess(caps, request, action)` | Check if capabilities grant access |
|
|
360
|
+
| `filterAccessibleNodes(nodes, caps, action)` | Filter nodes by access |
|
|
361
|
+
| `getMostSpecificCapability(caps, request)` | Get most specific matching capability |
|
|
362
|
+
| `parseResource(resource)` | Parse resource identifier |
|
|
363
|
+
| `formatResource(type, value)` | Format resource identifier |
|
|
364
|
+
| `capabilityGrantsAccess(cap, request, action)` | Check single capability |
|
|
365
|
+
|
|
366
|
+
## Configuration
|
|
367
|
+
|
|
368
|
+
### Head Meta Tags
|
|
369
|
+
|
|
370
|
+
```html
|
|
371
|
+
<!-- Default translation language -->
|
|
372
|
+
<meta name="aeon-language" content="es">
|
|
373
|
+
|
|
374
|
+
<!-- Translation endpoint -->
|
|
375
|
+
<meta name="aeon-translation-endpoint" content="https://ai-gateway.example.com">
|
|
376
|
+
|
|
377
|
+
<!-- Cache TTL in seconds -->
|
|
378
|
+
<meta name="aeon-translation-cache-ttl" content="86400">
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### ESI State
|
|
382
|
+
|
|
383
|
+
Global state is available at `window.__AEON_ESI_STATE__`:
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
interface GlobalESIState {
|
|
387
|
+
tier: 'free' | 'premium' | 'enterprise';
|
|
388
|
+
emotion: EmotionState;
|
|
389
|
+
preferences: {
|
|
390
|
+
language: string;
|
|
391
|
+
theme: 'light' | 'dark' | 'system';
|
|
392
|
+
};
|
|
393
|
+
features: Record<string, boolean>;
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## License
|
|
398
|
+
|
|
399
|
+
MIT
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build script for aeon-pages-runtime
|
|
3
|
+
*
|
|
4
|
+
* Marks React as external to prevent duplicate React instances
|
|
5
|
+
* when consumers bundle this package.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const entrypoints = [
|
|
9
|
+
'./src/index.ts',
|
|
10
|
+
'./src/server.ts',
|
|
11
|
+
'./src/router/index.ts',
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
async function build() {
|
|
15
|
+
const result = await Bun.build({
|
|
16
|
+
entrypoints,
|
|
17
|
+
outdir: './dist',
|
|
18
|
+
target: 'browser', // Changed from 'bun' to 'browser' for client compatibility
|
|
19
|
+
splitting: true,
|
|
20
|
+
format: 'esm',
|
|
21
|
+
external: [
|
|
22
|
+
// Mark React as external - consumers provide their own React
|
|
23
|
+
'react',
|
|
24
|
+
'react-dom',
|
|
25
|
+
'react/jsx-runtime',
|
|
26
|
+
'react/jsx-dev-runtime',
|
|
27
|
+
// Also mark peer dependencies as external
|
|
28
|
+
'zod',
|
|
29
|
+
'@affectively/aeon',
|
|
30
|
+
'@affectively/auth',
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (!result.success) {
|
|
35
|
+
console.error('Build failed:');
|
|
36
|
+
for (const log of result.logs) {
|
|
37
|
+
console.error(log);
|
|
38
|
+
}
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log('Build successful:');
|
|
43
|
+
for (const output of result.outputs) {
|
|
44
|
+
console.log(` ${output.path}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
build();
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@affectively/aeon-pages-runtime",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Bun/Cloudflare runtime for @affectively/aeon-pages",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/types.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./server": {
|
|
15
|
+
"import": "./dist/server.js",
|
|
16
|
+
"types": "./dist/server.d.ts"
|
|
17
|
+
},
|
|
18
|
+
"./router": {
|
|
19
|
+
"import": "./dist/router/index.js",
|
|
20
|
+
"types": "./dist/router/index.d.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "bun run build.ts && tsc --declaration --emitDeclarationOnly",
|
|
25
|
+
"dev": "bun --watch ./src/index.ts",
|
|
26
|
+
"test": "bun test",
|
|
27
|
+
"prepublishOnly": "bun run build",
|
|
28
|
+
"deploy": "wrangler deploy",
|
|
29
|
+
"deploy:staging": "wrangler deploy --env staging",
|
|
30
|
+
"dev:worker": "wrangler dev",
|
|
31
|
+
"db:create": "wrangler d1 create aeon-pages",
|
|
32
|
+
"db:migrate": "wrangler d1 execute aeon-pages --file=./schema.sql",
|
|
33
|
+
"db:migrate:local": "wrangler d1 execute aeon-pages --local --file=./schema.sql",
|
|
34
|
+
"tail": "wrangler tail"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"README.md"
|
|
39
|
+
],
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"bun": ">=1.0.0",
|
|
42
|
+
"react": ">=18.0.0",
|
|
43
|
+
"zod": ">=3.0.0",
|
|
44
|
+
"@affectively/aeon": ">=0.1.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"@affectively/aeon": {
|
|
48
|
+
"optional": true
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"typescript": "^5.7.0",
|
|
53
|
+
"@types/bun": "latest",
|
|
54
|
+
"@types/react": "^19.0.0",
|
|
55
|
+
"react": "^19.0.0",
|
|
56
|
+
"zod": "^3.24.0"
|
|
57
|
+
},
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "https://github.com/affectively/aeon-pages"
|
|
62
|
+
},
|
|
63
|
+
"keywords": [
|
|
64
|
+
"aeon",
|
|
65
|
+
"pages",
|
|
66
|
+
"framework",
|
|
67
|
+
"collaborative",
|
|
68
|
+
"bun",
|
|
69
|
+
"cloudflare"
|
|
70
|
+
]
|
|
71
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
-- Aeon Pages D1 Schema
|
|
2
|
+
-- Run with: wrangler d1 execute aeon-pages --file=./schema.sql
|
|
3
|
+
|
|
4
|
+
-- Sessions table (async propagation from Durable Objects)
|
|
5
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
6
|
+
session_id TEXT PRIMARY KEY,
|
|
7
|
+
route TEXT NOT NULL,
|
|
8
|
+
tree TEXT NOT NULL, -- JSON serialized component tree
|
|
9
|
+
data TEXT, -- JSON serialized session data
|
|
10
|
+
schema_version TEXT NOT NULL,
|
|
11
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
12
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
-- Routes table (backup for route registry)
|
|
16
|
+
CREATE TABLE IF NOT EXISTS routes (
|
|
17
|
+
pattern TEXT PRIMARY KEY,
|
|
18
|
+
session_id TEXT,
|
|
19
|
+
component_id TEXT,
|
|
20
|
+
is_aeon BOOLEAN DEFAULT FALSE,
|
|
21
|
+
metadata TEXT, -- JSON
|
|
22
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
23
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
-- Presence snapshots (for analytics)
|
|
27
|
+
CREATE TABLE IF NOT EXISTS presence_snapshots (
|
|
28
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29
|
+
session_id TEXT NOT NULL,
|
|
30
|
+
user_count INTEGER NOT NULL,
|
|
31
|
+
users TEXT, -- JSON array of user IDs
|
|
32
|
+
snapshot_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
33
|
+
FOREIGN KEY (session_id) REFERENCES sessions(session_id)
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
-- Indexes
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_route ON sessions(route);
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(updated_at);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_presence_session ON presence_snapshots(session_id);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_presence_time ON presence_snapshots(snapshot_at);
|