@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,556 @@
1
+ /**
2
+ * ESI Translation React Components
3
+ *
4
+ * Provides React components for automatic translation in aeon-flux applications.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * // Basic usage with explicit target language
9
+ * <ESI.Translate targetLanguage="es">
10
+ * Hello, welcome to our platform!
11
+ * </ESI.Translate>
12
+ *
13
+ * // Using TranslationProvider for app-wide language
14
+ * <TranslationProvider defaultLanguage="es">
15
+ * <ESI.Translate>Hello world</ESI.Translate>
16
+ * </TranslationProvider>
17
+ *
18
+ * // Programmatic translation
19
+ * const { translate, language } = useTranslation();
20
+ * const translated = await translate('Hello world');
21
+ * ```
22
+ */
23
+
24
+ import {
25
+ createContext,
26
+ useContext,
27
+ useEffect,
28
+ useState,
29
+ useCallback,
30
+ useMemo,
31
+ type ReactNode,
32
+ type FC,
33
+ } from 'react';
34
+ import type {
35
+ TranslationResult,
36
+ TranslationProviderConfig,
37
+ SupportedLanguageCode,
38
+ } from './types';
39
+ import {
40
+ esiTranslate,
41
+ generateTranslationCacheKey,
42
+ getCachedTranslation,
43
+ setCachedTranslation,
44
+ readHeadTranslationConfig,
45
+ detectTargetLanguage,
46
+ normalizeLanguageCode,
47
+ getSupportedLanguages,
48
+ getLanguageName,
49
+ } from './esi-translate';
50
+ import { useESI, useGlobalESIState } from './esi-react';
51
+
52
+ // ============================================================================
53
+ // Translation Context
54
+ // ============================================================================
55
+
56
+ export interface TranslationContextValue {
57
+ /** Current target language (ISO 639-1 code) */
58
+ language: string;
59
+
60
+ /** Set the target language */
61
+ setLanguage: (lang: string) => void;
62
+
63
+ /** Translate text programmatically */
64
+ translate: (
65
+ text: string,
66
+ options?: {
67
+ targetLanguage?: string;
68
+ sourceLanguage?: string;
69
+ context?: string;
70
+ },
71
+ ) => Promise<TranslationResult>;
72
+
73
+ /** Is translation currently loading */
74
+ isTranslating: boolean;
75
+
76
+ /** List of supported languages */
77
+ supportedLanguages: SupportedLanguageCode[];
78
+
79
+ /** Translation endpoint */
80
+ endpoint: string;
81
+
82
+ /** Cache TTL in seconds */
83
+ cacheTtl: number;
84
+ }
85
+
86
+ const TranslationContext = createContext<TranslationContextValue | null>(null);
87
+
88
+ // ============================================================================
89
+ // TranslationProvider
90
+ // ============================================================================
91
+
92
+ export interface TranslationProviderProps {
93
+ children: ReactNode;
94
+
95
+ /** Default target language (ISO 639-1 code or language name) */
96
+ defaultLanguage?: string;
97
+
98
+ /** AI Gateway endpoint for translation */
99
+ endpoint?: string;
100
+
101
+ /** Cache TTL in seconds (default: 86400 = 24 hours) */
102
+ cacheTtl?: number;
103
+
104
+ /** Fallback language if translation fails */
105
+ fallbackLanguage?: string;
106
+ }
107
+
108
+ /**
109
+ * TranslationProvider - Provides translation context to the component tree
110
+ *
111
+ * @example
112
+ * ```tsx
113
+ * function App() {
114
+ * return (
115
+ * <TranslationProvider defaultLanguage="es" endpoint="https://ai-gateway.example.com">
116
+ * <MyComponent />
117
+ * </TranslationProvider>
118
+ * );
119
+ * }
120
+ * ```
121
+ */
122
+ export const TranslationProvider: FC<TranslationProviderProps> = ({
123
+ children,
124
+ defaultLanguage,
125
+ endpoint: propEndpoint,
126
+ cacheTtl: propCacheTtl = 86400,
127
+ fallbackLanguage = 'en',
128
+ }) => {
129
+ // Read config from head tags
130
+ const headConfig = useMemo(() => readHeadTranslationConfig(), []);
131
+
132
+ // Get global ESI state for language preferences
133
+ const globalState = useGlobalESIState();
134
+
135
+ // Determine initial language
136
+ const initialLanguage = useMemo(
137
+ () =>
138
+ normalizeLanguageCode(
139
+ defaultLanguage ||
140
+ headConfig.defaultLanguage ||
141
+ globalState.preferences?.language ||
142
+ fallbackLanguage,
143
+ ),
144
+ [
145
+ defaultLanguage,
146
+ headConfig.defaultLanguage,
147
+ globalState.preferences?.language,
148
+ fallbackLanguage,
149
+ ],
150
+ );
151
+
152
+ const [language, setLanguageState] = useState(initialLanguage);
153
+ const [isTranslating, setIsTranslating] = useState(false);
154
+
155
+ // Resolve endpoint
156
+ const endpoint =
157
+ propEndpoint ||
158
+ headConfig.endpoint ||
159
+ 'https://ai-gateway.taylorbuley.workers.dev';
160
+ const cacheTtl = propCacheTtl || headConfig.cacheTtl || 86400;
161
+
162
+ // Get ESI context for processing
163
+ const esiContext = useESI();
164
+
165
+ // Set language handler
166
+ const setLanguage = useCallback((lang: string) => {
167
+ setLanguageState(normalizeLanguageCode(lang));
168
+ }, []);
169
+
170
+ // Translate function
171
+ const translate = useCallback(
172
+ async (
173
+ text: string,
174
+ options: {
175
+ targetLanguage?: string;
176
+ sourceLanguage?: string;
177
+ context?: string;
178
+ } = {},
179
+ ): Promise<TranslationResult> => {
180
+ const targetLang = normalizeLanguageCode(
181
+ options.targetLanguage || language,
182
+ );
183
+ const sourceLang = options.sourceLanguage || 'auto';
184
+
185
+ // Check cache
186
+ const cacheKey = generateTranslationCacheKey(
187
+ text,
188
+ sourceLang,
189
+ targetLang,
190
+ options.context,
191
+ );
192
+ const cached = getCachedTranslation(cacheKey);
193
+ if (cached) {
194
+ return cached;
195
+ }
196
+
197
+ setIsTranslating(true);
198
+
199
+ try {
200
+ // Create ESI directive
201
+ const directive = esiTranslate(text, targetLang, {
202
+ sourceLanguage: sourceLang,
203
+ context: options.context,
204
+ cacheTtl,
205
+ });
206
+
207
+ // Process via ESI
208
+ const esiResult = await esiContext.process(directive);
209
+
210
+ const result: TranslationResult = {
211
+ original: text,
212
+ translated: esiResult.success ? esiResult.output || text : text,
213
+ sourceLanguage: sourceLang,
214
+ targetLanguage: targetLang,
215
+ confidence: esiResult.success ? 0.95 : 0,
216
+ cached: esiResult.cached,
217
+ latencyMs: esiResult.latencyMs,
218
+ };
219
+
220
+ // Cache the result
221
+ if (esiResult.success) {
222
+ setCachedTranslation(cacheKey, result, cacheTtl);
223
+ }
224
+
225
+ return result;
226
+ } catch (error) {
227
+ // Return original text on error
228
+ return {
229
+ original: text,
230
+ translated: text,
231
+ sourceLanguage: sourceLang,
232
+ targetLanguage: targetLang,
233
+ confidence: 0,
234
+ cached: false,
235
+ latencyMs: 0,
236
+ };
237
+ } finally {
238
+ setIsTranslating(false);
239
+ }
240
+ },
241
+ [language, cacheTtl, esiContext],
242
+ );
243
+
244
+ const contextValue = useMemo(
245
+ () => ({
246
+ language,
247
+ setLanguage,
248
+ translate,
249
+ isTranslating,
250
+ supportedLanguages: getSupportedLanguages(),
251
+ endpoint,
252
+ cacheTtl,
253
+ }),
254
+ [language, setLanguage, translate, isTranslating, endpoint, cacheTtl],
255
+ );
256
+
257
+ return (
258
+ <TranslationContext.Provider value={contextValue}>
259
+ {children}
260
+ </TranslationContext.Provider>
261
+ );
262
+ };
263
+
264
+ // ============================================================================
265
+ // useTranslation Hook
266
+ // ============================================================================
267
+
268
+ /**
269
+ * Hook to access translation context
270
+ *
271
+ * @example
272
+ * ```tsx
273
+ * function MyComponent() {
274
+ * const { translate, language, setLanguage } = useTranslation();
275
+ *
276
+ * const handleTranslate = async () => {
277
+ * const result = await translate('Hello world');
278
+ * console.log(result.translated);
279
+ * };
280
+ *
281
+ * return (
282
+ * <button onClick={handleTranslate}>
283
+ * Translate (current: {language})
284
+ * </button>
285
+ * );
286
+ * }
287
+ * ```
288
+ */
289
+ export function useTranslation(): TranslationContextValue {
290
+ const ctx = useContext(TranslationContext);
291
+ if (!ctx) {
292
+ throw new Error('useTranslation must be used within a TranslationProvider');
293
+ }
294
+ return ctx;
295
+ }
296
+
297
+ /**
298
+ * Hook to access translation context (optional - returns null if not available)
299
+ */
300
+ export function useTranslationOptional(): TranslationContextValue | null {
301
+ return useContext(TranslationContext);
302
+ }
303
+
304
+ // ============================================================================
305
+ // ESI.Translate Component
306
+ // ============================================================================
307
+
308
+ export interface ESITranslateProps {
309
+ /** Content to translate */
310
+ children?: ReactNode;
311
+
312
+ /** Explicit text to translate (overrides children) */
313
+ text?: string;
314
+
315
+ /** Target language (ISO 639-1 code or language name) */
316
+ targetLanguage?: string;
317
+
318
+ /** Source language (ISO 639-1 code, or 'auto' for auto-detect) */
319
+ sourceLanguage?: string;
320
+
321
+ /** Additional context for better translation quality */
322
+ context?: string;
323
+
324
+ /** Fallback content if translation fails */
325
+ fallback?: ReactNode;
326
+
327
+ /** Loading content */
328
+ loading?: ReactNode;
329
+
330
+ /** Cache TTL in seconds (default: 86400 = 24 hours) */
331
+ cacheTtl?: number;
332
+
333
+ /** Enable streaming translation */
334
+ stream?: boolean;
335
+
336
+ /** Custom render function */
337
+ render?: (result: TranslationResult) => ReactNode;
338
+
339
+ /** Class name for wrapper */
340
+ className?: string;
341
+
342
+ /** HTML element to render as */
343
+ as?: 'span' | 'div' | 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
344
+
345
+ /** Callback when translation completes */
346
+ onComplete?: (result: TranslationResult) => void;
347
+
348
+ /** Callback on error */
349
+ onError?: (error: string) => void;
350
+ }
351
+
352
+ /**
353
+ * ESI Translation Component
354
+ *
355
+ * Translates text to the specified language using AI inference.
356
+ * Uses TranslationProvider context if available, otherwise falls back
357
+ * to detecting language from various sources.
358
+ *
359
+ * @example
360
+ * ```tsx
361
+ * // Basic usage
362
+ * <ESI.Translate targetLanguage="es">
363
+ * Hello, how are you?
364
+ * </ESI.Translate>
365
+ *
366
+ * // With context for better translation
367
+ * <ESI.Translate targetLanguage="ja" context="emotional wellness app">
368
+ * We're here to help you understand your feelings.
369
+ * </ESI.Translate>
370
+ *
371
+ * // Using language from context
372
+ * <ESI.Translate>
373
+ * Welcome to our platform
374
+ * </ESI.Translate>
375
+ * ```
376
+ */
377
+ export const ESITranslate: FC<ESITranslateProps> = ({
378
+ children,
379
+ text,
380
+ targetLanguage: propTargetLanguage,
381
+ sourceLanguage = 'auto',
382
+ context,
383
+ fallback,
384
+ loading = '...',
385
+ cacheTtl = 86400,
386
+ stream = false,
387
+ render,
388
+ className,
389
+ as: Component = 'span',
390
+ onComplete,
391
+ onError,
392
+ }) => {
393
+ const { process, processWithStream, enabled } = useESI();
394
+ const globalState = useGlobalESIState();
395
+ const translationContext = useTranslationOptional();
396
+
397
+ const [output, setOutput] = useState<string>('');
398
+ const [isLoading, setIsLoading] = useState(true);
399
+ const [error, setError] = useState<string | null>(null);
400
+ const [result, setResult] = useState<TranslationResult | null>(null);
401
+
402
+ // Get text to translate
403
+ const textToTranslate = useMemo(
404
+ () =>
405
+ text ||
406
+ (typeof children === 'string' ? children : String(children || '')),
407
+ [text, children],
408
+ );
409
+
410
+ // Determine target language
411
+ const targetLanguage = useMemo(
412
+ () =>
413
+ normalizeLanguageCode(
414
+ propTargetLanguage ||
415
+ translationContext?.language ||
416
+ detectTargetLanguage(undefined, globalState),
417
+ ),
418
+ [propTargetLanguage, translationContext?.language, globalState],
419
+ );
420
+
421
+ // Skip translation if source and target are the same
422
+ const shouldTranslate = useMemo(() => {
423
+ if (!textToTranslate) return false;
424
+ // Don't translate if target is 'en' and we assume source is English
425
+ // (this is a heuristic - could be improved with language detection)
426
+ if (targetLanguage === 'en' && sourceLanguage === 'auto') return false;
427
+ if (sourceLanguage === targetLanguage) return false;
428
+ return true;
429
+ }, [textToTranslate, targetLanguage, sourceLanguage]);
430
+
431
+ useEffect(() => {
432
+ // If translation is disabled or not needed
433
+ if (!enabled || !shouldTranslate) {
434
+ setOutput(textToTranslate);
435
+ setIsLoading(false);
436
+ return;
437
+ }
438
+
439
+ // Check cache
440
+ const cacheKey = generateTranslationCacheKey(
441
+ textToTranslate,
442
+ sourceLanguage,
443
+ targetLanguage,
444
+ context,
445
+ );
446
+ const cached = getCachedTranslation(cacheKey);
447
+ if (cached) {
448
+ setOutput(cached.translated);
449
+ setResult(cached);
450
+ setIsLoading(false);
451
+ onComplete?.(cached);
452
+ return;
453
+ }
454
+
455
+ // Create translation directive
456
+ const directive = esiTranslate(textToTranslate, targetLanguage, {
457
+ sourceLanguage,
458
+ context,
459
+ cacheTtl,
460
+ });
461
+
462
+ const processTranslation = async () => {
463
+ try {
464
+ let esiResult;
465
+
466
+ if (stream && processWithStream) {
467
+ setOutput('');
468
+ esiResult = await processWithStream(directive, (chunk) => {
469
+ setOutput((prev) => prev + chunk);
470
+ });
471
+ } else {
472
+ esiResult = await process(directive);
473
+ }
474
+
475
+ setIsLoading(false);
476
+
477
+ if (esiResult.success && esiResult.output) {
478
+ const translatedText = esiResult.output.trim();
479
+ setOutput(translatedText);
480
+
481
+ const translationResult: TranslationResult = {
482
+ original: textToTranslate,
483
+ translated: translatedText,
484
+ sourceLanguage,
485
+ targetLanguage,
486
+ confidence: 0.95,
487
+ cached: esiResult.cached,
488
+ latencyMs: esiResult.latencyMs,
489
+ };
490
+
491
+ setResult(translationResult);
492
+ setCachedTranslation(cacheKey, translationResult, cacheTtl);
493
+ onComplete?.(translationResult);
494
+ } else {
495
+ setError(esiResult.error || 'Translation failed');
496
+ setOutput(textToTranslate); // Fall back to original
497
+ onError?.(esiResult.error || 'Translation failed');
498
+ }
499
+ } catch (err) {
500
+ setIsLoading(false);
501
+ setOutput(textToTranslate);
502
+ const errorMsg =
503
+ err instanceof Error ? err.message : 'Translation failed';
504
+ setError(errorMsg);
505
+ onError?.(errorMsg);
506
+ }
507
+ };
508
+
509
+ processTranslation();
510
+ }, [
511
+ textToTranslate,
512
+ targetLanguage,
513
+ sourceLanguage,
514
+ context,
515
+ cacheTtl,
516
+ enabled,
517
+ shouldTranslate,
518
+ stream,
519
+ process,
520
+ processWithStream,
521
+ onComplete,
522
+ onError,
523
+ ]);
524
+
525
+ // Render loading state
526
+ if (isLoading && !stream) {
527
+ return <Component className={className}>{loading}</Component>;
528
+ }
529
+
530
+ // Render error with fallback
531
+ if (error && fallback) {
532
+ return <Component className={className}>{fallback}</Component>;
533
+ }
534
+
535
+ // Custom render
536
+ if (render && result) {
537
+ return <Component className={className}>{render(result)}</Component>;
538
+ }
539
+
540
+ return (
541
+ <Component className={className}>
542
+ {output || (isLoading ? loading : '')}
543
+ </Component>
544
+ );
545
+ };
546
+
547
+ // ============================================================================
548
+ // Exports
549
+ // ============================================================================
550
+
551
+ export {
552
+ TranslationContext,
553
+ normalizeLanguageCode,
554
+ getSupportedLanguages,
555
+ getLanguageName,
556
+ };