@cruxjs/client 0.0.1 → 0.0.3

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/README.md CHANGED
@@ -8,12 +8,12 @@
8
8
  </div>
9
9
 
10
10
  <div align="center">
11
- <img src="https://img.shields.io/badge/v-0.0.1-black"/>
11
+ <img src="https://img.shields.io/badge/v-0.0.3-black"/>
12
12
  <img src="https://img.shields.io/badge/🔥-@cruxjs-black"/>
13
13
  <br>
14
14
  <img src="https://img.shields.io/badge/coverage----%25-brightgreen" alt="Test Coverage" />
15
- <img src="https://img.shields.io/github/issues/cruxjs/client?style=flat" alt="Github Repo Issues" />
16
- <img src="https://img.shields.io/github/stars/cruxjs/client?style=social" alt="GitHub Repo stars" />
15
+ <img src="https://img.shields.io/github/issues/cruxjs-org/client?style=flat" alt="Github Repo Issues" />
16
+ <img src="https://img.shields.io/github/stars/cruxjs-org/client?style=social" alt="GitHub Repo stars" />
17
17
  </div>
18
18
  <br>
19
19
 
@@ -73,6 +73,40 @@
73
73
  // App is live and routing is reactive
74
74
  ```
75
75
 
76
+ - ### 1.5 With Internationalization (i18n)
77
+
78
+ > Setup multi-language support with translations:
79
+
80
+ ```typescript
81
+ import { ClientManager } from '@cruxjs/client';
82
+
83
+ const HomePage = () => <div>Welcome Home</div>;
84
+ const NotFoundPage = () => <div>404 - Not Found</div>;
85
+
86
+ // Create manager with i18n config
87
+ const app = new ClientManager({
88
+ routes: {
89
+ '/': HomePage
90
+ },
91
+ notFoundComponent: NotFoundPage,
92
+ i18n: {
93
+ defaultLanguage: 'en',
94
+ supportedLanguages: ['en', 'ar'],
95
+ // TODO: short it
96
+ basePath: 'http://localhost:3000/static/dist/i18n' // Path to translation files
97
+ },
98
+ debug: true
99
+ });
100
+
101
+ // Boot and ready
102
+ await app.boot();
103
+ await app.ready('#app');
104
+
105
+ // Now use translations in components or hooks
106
+ const { t } = app.getI18n();
107
+ console.log(t('welcome')); // Gets translation key
108
+ ```
109
+
76
110
  - ### 2. With Lifecycle Hooks
77
111
 
78
112
  ```typescript
@@ -391,6 +425,51 @@
391
425
  }
392
426
  ```
393
427
 
428
+ ### Internationalization (i18n)
429
+
430
+ - #### `getI18n(): I18nInstance`
431
+ > Access the i18n instance for translations
432
+ > - Returns `undefined` if i18n not initialized
433
+ > - Call after `boot()` if i18n config provided
434
+
435
+ ```typescript
436
+ const i18n = app.getI18n();
437
+ if (i18n) {
438
+ const greeting = i18n.t('greeting');
439
+ console.log(greeting);
440
+ }
441
+ ```
442
+
443
+ - #### `getTranslation(key: string, defaultValue?: string): string`
444
+ > Safely get a translation with fallback
445
+ > - Returns default value if key not found
446
+ > - Returns key if i18n not initialized
447
+ > - Safe to call anytime
448
+
449
+ ```typescript
450
+ // In lifecycle hook or component
451
+ const message = app.getTranslation('welcome', 'Welcome!');
452
+ console.log(message); // 'Welcome!' if not found in i18n
453
+ ```
454
+
455
+ ### i18n Utilities
456
+
457
+ - #### `useTranslation(): { t: (key: string, defaultValue?: string) => string }`
458
+ > Composable hook to use translations in components
459
+ > - Safe even if i18n not initialized
460
+ > - Returns both `t()` and `getTranslation()` methods
461
+
462
+ ```typescript
463
+ import { useTranslation } from '@cruxjs/client';
464
+
465
+ // In your components
466
+ const { t } = useTranslation();
467
+
468
+ const HomePage = () => {
469
+ return <div>{t('home.title', 'Welcome')}</div>;
470
+ };
471
+ ```
472
+
394
473
  <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
395
474
 
396
475
 
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var signals=require('@minejs/signals'),browser=require('@minejs/browser');var i=class{constructor(e){this.lifecycle="booting";this.hooks={};this.routeComponents={};this.currentPathSignal=signals.signal(window.location.pathname??"/");this.config=e,this.debug=e.debug??false,this.log("[INIT] Creating ClientManager"),this.routeComponents=e.routes,this.eventsManager=new browser.EventsManager,this.windowManager=new browser.WindowManager;let t=Object.entries(e.routes).map(([o,n])=>({path:o,component:n}));this.router=browser.createRouter({routes:t,notFoundComponent:e.notFoundComponent}),this.router.afterEach(o=>{this.currentPathSignal.set(o.path);}),this.log("[INIT] ClientManager created");}on(e,t,o,n){return typeof e=="string"&&e.startsWith("on")?(this.hooks[e]=t,this):this.eventsManager.on(e,t,o,n)}async boot(){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Already booted or destroyed");return}this.log("\u26A1 Phase: BOOT");try{this.hooks.onBoot&&(this.log("\u2192 Calling onBoot hook"),await this.hooks.onBoot()),this.log("\u2713 BOOT phase complete");}catch(e){throw console.error("[ClientManager] Boot failed:",e),e}}async ready(e){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Cannot ready - not in booting phase");return}this.log("\u26A1 Phase: READY");try{this.mount(e),this.log("\u2192 Router mounted"),this.debug&&(globalThis.__CLIENT_MANAGER__=this,this.log("\u2192 ClientManager available globally")),this.hooks.onReady&&(this.log("\u2192 Calling onReady hook"),await this.hooks.onReady()),this.lifecycle="ready",this.log("\u2713 READY phase complete"),this.log("\u2713 App is ready!");}catch(t){throw console.error("[ClientManager] Ready failed:",t),t}}async destroy(){if(this.lifecycle==="destroyed"){console.warn("[ClientManager] Already destroyed");return}this.lifecycle="destroying",this.log("\u26A1 Phase: DESTROY");try{this.hooks.onDestroy&&(this.log("\u2192 Calling onDestroy hook"),await this.hooks.onDestroy()),this.eventsManager.destroy(),this.windowManager.destroy(),this.lifecycle="destroyed",this.log("\u2713 DESTROY phase complete");}catch(e){throw console.error("[ClientManager] Destroy failed:",e),e}}navigate(e){this.router.push(e);}mount(e){let t=typeof e=="string"?document.querySelector(e):e;if(!t){console.warn("[ClientManager] Mount target not found:",e);return}signals.effect(()=>{let o=this.currentPathSignal(),n=this.routeComponents[o]||this.config.notFoundComponent||null;if(t.innerHTML="",n){let r=n();r instanceof Node&&t.appendChild(r);}else t.innerHTML="<p>No component found for this route</p>";this.log(`\u2192 Route changed to: ${o}`);}),this.log("\u2192 Routing setup complete");}getCurrentPath(){return this.currentPathSignal}createLinkHandler(e){return t=>{t.preventDefault(),this.navigate(e);}}getRouter(){return this.router}off(e,t,o){this.eventsManager.off(e,t,o);}getEventsManager(){return this.eventsManager}getViewport(){return this.windowManager.getViewport()}getWindowManager(){return this.windowManager}getPhase(){return this.lifecycle}isReady(){return this.lifecycle==="ready"}log(e){this.debug&&console.log(`[ClientManager] ${e}`);}};exports.ClientManager=i;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';var signals=require('@minejs/signals'),i18n=require('@minejs/i18n'),browser=require('@minejs/browser');var a=class{constructor(t){this.lifecycle="booting";this.hooks={};this.routeComponents={};this.currentPathSignal=signals.signal(window.location.pathname??"/");this.config=t,this.debug=t.debug??false,this.log("[INIT] Creating ClientManager"),this.routeComponents=t.routes,this.eventsManager=new browser.EventsManager,this.windowManager=new browser.WindowManager;let e=Object.entries(t.routes).map(([n,o])=>({path:n,component:o}));this.router=browser.createRouter({routes:e,notFoundComponent:t.notFoundComponent}),this.router.afterEach(n=>{this.currentPathSignal.set(n.path);}),this.log("[INIT] ClientManager created");}on(t,e,n,o){return typeof t=="string"&&t.startsWith("on")?(this.hooks[t]=e,this):this.eventsManager.on(t,e,n,o)}async boot(){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Already booted or destroyed");return}this.log("\u26A1 Phase: BOOT");try{this.config.i18n&&await this.setupI18n(this.config.i18n),this.hooks.onBoot&&(this.log("\u2192 Calling onBoot hook"),await this.hooks.onBoot()),this.log("\u2713 BOOT phase complete");}catch(t){throw console.error("[ClientManager] Boot failed:",t),t}}async ready(t){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Cannot ready - not in booting phase");return}this.log("\u26A1 Phase: READY");try{this.mount(t),this.log("\u2192 Router mounted"),this.debug&&(globalThis.__CLIENT_MANAGER__=this,this.log("\u2192 ClientManager available globally")),this.hooks.onReady&&(this.log("\u2192 Calling onReady hook"),await this.hooks.onReady()),this.lifecycle="ready",this.log("\u2713 READY phase complete"),this.log("\u2713 App is ready!");}catch(e){throw console.error("[ClientManager] Ready failed:",e),e}}async destroy(){if(this.lifecycle==="destroyed"){console.warn("[ClientManager] Already destroyed");return}this.lifecycle="destroying",this.log("\u26A1 Phase: DESTROY");try{this.hooks.onDestroy&&(this.log("\u2192 Calling onDestroy hook"),await this.hooks.onDestroy()),this.eventsManager.destroy(),this.windowManager.destroy(),this.lifecycle="destroyed",this.log("\u2713 DESTROY phase complete");}catch(t){throw console.error("[ClientManager] Destroy failed:",t),t}}async setupI18n(t){this.log("Setting up i18n...");try{await i18n.setupAuto({defaultLanguage:t.defaultLanguage,supportedLanguages:t.supportedLanguages,basePath:t.basePath,fileExtension:t.fileExtension||"json"}),this.log(`i18n ready \u2192 ${t.supportedLanguages.join(", ")}`);}catch(e){throw this.log("Failed to setup i18n"+e.message),e}}navigate(t){this.router.push(t);}mount(t){let e=typeof t=="string"?document.querySelector(t):t;if(!e){console.warn("[ClientManager] Mount target not found:",t);return}signals.effect(()=>{let n=this.currentPathSignal(),o=this.routeComponents[n]||this.config.notFoundComponent||null;if(e.innerHTML="",o){let s=o();s instanceof Node&&e.appendChild(s);}else e.innerHTML="<p>No component found for this route</p>";this.log(`\u2192 Route changed to: ${n}`);}),this.log("\u2192 Routing setup complete");}getCurrentPath(){return this.currentPathSignal}createLinkHandler(t){return e=>{e.preventDefault(),this.navigate(t);}}getRouter(){return this.router}off(t,e,n){this.eventsManager.off(t,e,n);}getEventsManager(){return this.eventsManager}getViewport(){return this.windowManager.getViewport()}getWindowManager(){return this.windowManager}getI18n(){return i18n.getI18n()}getTranslation(t,e){let n=i18n.getI18n();return n?n.t(t)??e??t:(console.warn("[ClientManager] i18n not initialized. Using default value or key."),e??t)}getPhase(){return this.lifecycle}isReady(){return this.lifecycle==="ready"}log(t){this.debug&&console.log(`[ClientManager] ${t}`);}};function C(){let r=i18n.getI18n();return {getTranslation:(t,e)=>r?r.t(t)??e??t:e??t,t:(t,e)=>r?r.t(t)??e??t:e??t}}exports.ClientManager=a;exports.useTranslation=C;//# sourceMappingURL=index.cjs.map
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["ClientManager","config","signal","EventsManager","WindowManager","routesArray","path","component","createRouter","to","eventOrTarget","callbackOrEvent","handler","options","err","mountSelector","selector","container","effect","currentPath","Component","node","e","target","event","message"],"mappings":"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","file":"index.cjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { signal, effect } from '@minejs/signals';\r\n import { EventsManager, Router, WindowManager, createRouter } from '@minejs/browser';\r\n import * as types from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n export class ClientManager {\r\n\r\n // ┌──────────────────────────────── INIT ──────────────────────────────┐\r\n\r\n private router : Router;\r\n private eventsManager : EventsManager;\r\n private windowManager : WindowManager;\r\n private lifecycle : 'booting' | 'ready' | 'destroying' | 'destroyed' = 'booting';\r\n private config : types.ClientManagerConfig;\r\n private hooks : types.ClientManagerHooks = {};\r\n private debug : boolean;\r\n private routeComponents : Record<string, types.RouteComponent> = {};\r\n private currentPathSignal = signal<string>(window.location.pathname ?? '/');\r\n\r\n constructor(config: types.ClientManagerConfig) {\r\n this.config = config;\r\n this.debug = config.debug ?? false;\r\n\r\n this.log('[INIT] Creating ClientManager');\r\n\r\n // Store route components provided by user\r\n this.routeComponents = config.routes;\r\n\r\n // Initialize managers from @minejs/browser\r\n this.eventsManager = new EventsManager();\r\n this.windowManager = new WindowManager();\r\n\r\n // Initialize router with user-provided routes\r\n const routesArray = Object.entries(config.routes).map(([path, component]) => ({\r\n path,\r\n component\r\n }));\r\n\r\n this.router = createRouter({\r\n routes: routesArray,\r\n notFoundComponent: config.notFoundComponent,\r\n });\r\n\r\n // Connect router changes to signal for automatic re-rendering\r\n this.router.afterEach((to) => {\r\n this.currentPathSignal.set(to.path);\r\n });\r\n\r\n this.log('[INIT] ClientManager created');\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Setup lifecycle hooks OR bind events\r\n * Overloaded: on(event: 'onBoot'|'onReady'|'onDestroy', callback) - lifecycle\r\n * on(target, event, handler) - event binding\r\n */\r\n on(event: keyof types.ClientManagerHooks, callback: any): this;\r\n on<K extends keyof HTMLElementEventMap>(\r\n target: EventTarget,\r\n event: K | string,\r\n handler: EventListener,\r\n options?: AddEventListenerOptions\r\n ): () => void;\r\n on(\r\n eventOrTarget: keyof types.ClientManagerHooks | EventTarget,\r\n callbackOrEvent?: any,\r\n handler?: EventListener,\r\n options?: AddEventListenerOptions\r\n ): this | (() => void) {\r\n // Check if this is a lifecycle hook call\r\n if (typeof eventOrTarget === 'string' && eventOrTarget.startsWith('on')) {\r\n this.hooks[eventOrTarget as keyof types.ClientManagerHooks] = callbackOrEvent;\r\n return this;\r\n }\r\n\r\n // Otherwise it's an event binding\r\n return this.eventsManager.on(\r\n eventOrTarget as EventTarget,\r\n callbackOrEvent as string,\r\n handler as EventListener,\r\n options\r\n );\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Bootstrap the app - Phase 1: BOOT\r\n */\r\n async boot(): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Already booted or destroyed');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: BOOT');\r\n\r\n try {\r\n // Call user onBoot hook\r\n if (this.hooks.onBoot) {\r\n this.log('→ Calling onBoot hook');\r\n await this.hooks.onBoot();\r\n }\r\n\r\n this.log('✓ BOOT phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Boot failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Ready the app - Phase 2: READY\r\n * Mount to DOM and make everything live\r\n */\r\n async ready(mountSelector: string | HTMLElement): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Cannot ready - not in booting phase');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: READY');\r\n\r\n try {\r\n // Mount router\r\n this.mount(mountSelector);\r\n this.log('→ Router mounted');\r\n\r\n // Setup global access for debugging\r\n if (this.debug) {\r\n (globalThis as any).__CLIENT_MANAGER__ = this;\r\n this.log('→ ClientManager available globally');\r\n }\r\n\r\n // Call user onReady hook\r\n if (this.hooks.onReady) {\r\n this.log('→ Calling onReady hook');\r\n await this.hooks.onReady();\r\n }\r\n\r\n this.lifecycle = 'ready';\r\n this.log('✓ READY phase complete');\r\n this.log('✓ App is ready!');\r\n } catch (err) {\r\n console.error('[ClientManager] Ready failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Shutdown the app - Phase 3: DESTROY\r\n */\r\n async destroy(): Promise<void> {\r\n if (this.lifecycle === 'destroyed') {\r\n console.warn('[ClientManager] Already destroyed');\r\n return;\r\n }\r\n\r\n this.lifecycle = 'destroying';\r\n this.log('⚡ Phase: DESTROY');\r\n\r\n try {\r\n // Call user onDestroy hook\r\n if (this.hooks.onDestroy) {\r\n this.log('→ Calling onDestroy hook');\r\n await this.hooks.onDestroy();\r\n }\r\n\r\n // Cleanup managers\r\n this.eventsManager.destroy();\r\n this.windowManager.destroy();\r\n\r\n this.lifecycle = 'destroyed';\r\n this.log('✓ DESTROY phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Destroy failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Navigate to path\r\n */\r\n navigate(path: string): void {\r\n this.router.push(path);\r\n }\r\n\r\n /**\r\n * Mount router to DOM element and setup reactive routing\r\n * Automatically re-renders when route changes\r\n */\r\n mount(selector: string | HTMLElement): void {\r\n const container = typeof selector === 'string'\r\n ? document.querySelector(selector)\r\n : selector;\r\n\r\n if (!container) {\r\n console.warn('[ClientManager] Mount target not found:', selector);\r\n return;\r\n }\r\n\r\n // Setup reactive routing effect - re-renders when currentPathSignal changes\r\n effect(() => {\r\n const currentPath = this.currentPathSignal();\r\n const Component = this.routeComponents[currentPath]\r\n || this.config.notFoundComponent\r\n || null;\r\n\r\n // Clear and mount new component\r\n container.innerHTML = '';\r\n\r\n if (Component) {\r\n const node = Component();\r\n if (node instanceof Node) {\r\n container.appendChild(node);\r\n }\r\n } else {\r\n container.innerHTML = '<p>No component found for this route</p>';\r\n }\r\n\r\n this.log(`→ Route changed to: ${currentPath}`);\r\n });\r\n\r\n this.log('→ Routing setup complete');\r\n }\r\n\r\n /**\r\n * Get current path signal for reactivity\r\n */\r\n getCurrentPath() {\r\n return this.currentPathSignal;\r\n }\r\n\r\n /**\r\n * Create navigation link handler\r\n */\r\n createLinkHandler(path: string) {\r\n return (e: MouseEvent) => {\r\n e.preventDefault();\r\n this.navigate(path);\r\n };\r\n }\r\n\r\n /**\r\n * Get underlying router for advanced usage\r\n */\r\n getRouter() {\r\n return this.router;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Unbind event\r\n */\r\n off(target: EventTarget, event: string, handler: EventListener): void {\r\n this.eventsManager.off(target, event, handler);\r\n }\r\n\r\n /**\r\n * Get events manager directly\r\n */\r\n getEventsManager(): EventsManager {\r\n return this.eventsManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get viewport info as reactive signal\r\n */\r\n getViewport() {\r\n return this.windowManager.getViewport();\r\n }\r\n\r\n /**\r\n * Get window manager directly\r\n */\r\n getWindowManager(): WindowManager {\r\n return this.windowManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get lifecycle phase\r\n */\r\n getPhase() {\r\n return this.lifecycle;\r\n }\r\n\r\n /**\r\n * Check if ready\r\n */\r\n isReady(): boolean {\r\n return this.lifecycle === 'ready';\r\n }\r\n\r\n /**\r\n * Internal logging\r\n */\r\n private log(message: string): void {\r\n if (this.debug) {\r\n console.log(`[ClientManager] ${message}`);\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["ClientManager","config","signal","EventsManager","WindowManager","routesArray","path","component","createRouter","to","eventOrTarget","callbackOrEvent","handler","options","err","mountSelector","setupAuto","selector","container","effect","currentPath","Component","node","target","event","getI18n","key","defaultValue","i18n","message","useTranslation"],"mappings":"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","file":"index.cjs","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { signal, effect } from '@minejs/signals';\r\n import { I18nConfig, setupAuto, getI18n } from '@minejs/i18n';\r\n import { EventsManager, Router, WindowManager, createRouter } from '@minejs/browser';\r\n import * as types from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n export class ClientManager {\r\n\r\n // ┌──────────────────────────────── INIT ──────────────────────────────┐\r\n\r\n private router : Router;\r\n private eventsManager : EventsManager;\r\n private windowManager : WindowManager;\r\n private lifecycle : 'booting' | 'ready' | 'destroying' | 'destroyed' = 'booting';\r\n private config : types.ClientManagerConfig;\r\n private hooks : types.ClientManagerHooks = {};\r\n private debug : boolean;\r\n private routeComponents : Record<string, types.RouteComponent> = {};\r\n private currentPathSignal = signal<string>(window.location.pathname ?? '/');\r\n\r\n constructor(config: types.ClientManagerConfig) {\r\n this.config = config;\r\n this.debug = config.debug ?? false;\r\n\r\n this.log('[INIT] Creating ClientManager');\r\n\r\n // Store route components provided by user\r\n this.routeComponents = config.routes;\r\n\r\n // Initialize managers from @minejs/browser\r\n this.eventsManager = new EventsManager();\r\n this.windowManager = new WindowManager();\r\n\r\n // Initialize router with user-provided routes\r\n const routesArray = Object.entries(config.routes).map(([path, component]) => ({\r\n path,\r\n component\r\n }));\r\n\r\n this.router = createRouter({\r\n routes: routesArray,\r\n notFoundComponent: config.notFoundComponent,\r\n });\r\n\r\n // Connect router changes to signal for automatic re-rendering\r\n this.router.afterEach((to) => {\r\n this.currentPathSignal.set(to.path);\r\n });\r\n\r\n this.log('[INIT] ClientManager created');\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Setup lifecycle hooks OR bind events\r\n * Overloaded: on(event: 'onBoot'|'onReady'|'onDestroy', callback) - lifecycle\r\n * on(target, event, handler) - event binding\r\n */\r\n on(event: keyof types.ClientManagerHooks, callback: any): this;\r\n on<K extends keyof HTMLElementEventMap>(\r\n target: EventTarget,\r\n event: K | string,\r\n handler: EventListener,\r\n options?: AddEventListenerOptions\r\n ): () => void;\r\n on(\r\n eventOrTarget: keyof types.ClientManagerHooks | EventTarget,\r\n callbackOrEvent?: any,\r\n handler?: EventListener,\r\n options?: AddEventListenerOptions\r\n ): this | (() => void) {\r\n // Check if this is a lifecycle hook call\r\n if (typeof eventOrTarget === 'string' && eventOrTarget.startsWith('on')) {\r\n this.hooks[eventOrTarget as keyof types.ClientManagerHooks] = callbackOrEvent;\r\n return this;\r\n }\r\n\r\n // Otherwise it's an event binding\r\n return this.eventsManager.on(\r\n eventOrTarget as EventTarget,\r\n callbackOrEvent as string,\r\n handler as EventListener,\r\n options\r\n );\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Bootstrap the app - Phase 1: BOOT\r\n */\r\n async boot(): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Already booted or destroyed');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: BOOT');\r\n\r\n try {\r\n // Setup i18n\r\n if (this.config.i18n)\r\n await this.setupI18n(this.config.i18n!);\r\n\r\n // Call user onBoot hook\r\n if (this.hooks.onBoot) {\r\n this.log('→ Calling onBoot hook');\r\n await this.hooks.onBoot();\r\n }\r\n\r\n this.log('✓ BOOT phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Boot failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Ready the app - Phase 2: READY\r\n * Mount to DOM and make everything live\r\n */\r\n async ready(mountSelector: string | HTMLElement): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Cannot ready - not in booting phase');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: READY');\r\n\r\n try {\r\n // Mount router\r\n this.mount(mountSelector);\r\n this.log('→ Router mounted');\r\n\r\n // Setup global access for debugging\r\n if (this.debug) {\r\n (globalThis as any).__CLIENT_MANAGER__ = this;\r\n this.log('→ ClientManager available globally');\r\n }\r\n\r\n // Call user onReady hook\r\n if (this.hooks.onReady) {\r\n this.log('→ Calling onReady hook');\r\n await this.hooks.onReady();\r\n }\r\n\r\n this.lifecycle = 'ready';\r\n this.log('✓ READY phase complete');\r\n this.log('✓ App is ready!');\r\n } catch (err) {\r\n console.error('[ClientManager] Ready failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Shutdown the app - Phase 3: DESTROY\r\n */\r\n async destroy(): Promise<void> {\r\n if (this.lifecycle === 'destroyed') {\r\n console.warn('[ClientManager] Already destroyed');\r\n return;\r\n }\r\n\r\n this.lifecycle = 'destroying';\r\n this.log('⚡ Phase: DESTROY');\r\n\r\n try {\r\n // Call user onDestroy hook\r\n if (this.hooks.onDestroy) {\r\n this.log('→ Calling onDestroy hook');\r\n await this.hooks.onDestroy();\r\n }\r\n\r\n // Cleanup managers\r\n this.eventsManager.destroy();\r\n this.windowManager.destroy();\r\n\r\n this.lifecycle = 'destroyed';\r\n this.log('✓ DESTROY phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Destroy failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Initializes internationalization (i18n) support\r\n *\r\n * Loads language files and configures the i18n system based on:\r\n * - Default language\r\n * - Supported languages\r\n * - Base path for translation files\r\n * - File extension (json, cjson, etc.)\r\n *\r\n * @param {AppConfig} config - Application configuration with i18n settings\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<void>}\r\n * @throws {Error} If i18n setup or language file loading fails\r\n *\r\n * @example\r\n * await setupI18n({\r\n * i18n: {\r\n * defaultLanguage: 'en',\r\n * supportedLanguages: ['en', 'ar'],\r\n * basePath: './src/i18n'\r\n * }\r\n * }, logger);\r\n */\r\n async setupI18n(config: I18nConfig) {\r\n this.log('Setting up i18n...');\r\n\r\n try {\r\n await setupAuto({\r\n defaultLanguage: config.defaultLanguage,\r\n supportedLanguages: config.supportedLanguages,\r\n basePath: config.basePath!,\r\n fileExtension: config.fileExtension || 'json'\r\n });\r\n\r\n this.log(`i18n ready → ${config.supportedLanguages!.join(', ')}`);\r\n } catch (err) {\r\n this.log('Failed to setup i18n' + (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Navigate to path\r\n */\r\n navigate(path: string): void {\r\n this.router.push(path);\r\n }\r\n\r\n /**\r\n * Mount router to DOM element and setup reactive routing\r\n * Automatically re-renders when route changes\r\n */\r\n mount(selector: string | HTMLElement): void {\r\n const container = typeof selector === 'string'\r\n ? document.querySelector(selector)\r\n : selector;\r\n\r\n if (!container) {\r\n console.warn('[ClientManager] Mount target not found:', selector);\r\n return;\r\n }\r\n\r\n // Setup reactive routing effect - re-renders when currentPathSignal changes\r\n effect(() => {\r\n const currentPath = this.currentPathSignal();\r\n const Component = this.routeComponents[currentPath]\r\n || this.config.notFoundComponent\r\n || null;\r\n\r\n // Clear and mount new component\r\n container.innerHTML = '';\r\n\r\n if (Component) {\r\n const node = Component();\r\n if (node instanceof Node) {\r\n container.appendChild(node);\r\n }\r\n } else {\r\n container.innerHTML = '<p>No component found for this route</p>';\r\n }\r\n\r\n this.log(`→ Route changed to: ${currentPath}`);\r\n });\r\n\r\n this.log('→ Routing setup complete');\r\n }\r\n\r\n /**\r\n * Get current path signal for reactivity\r\n */\r\n getCurrentPath() {\r\n return this.currentPathSignal;\r\n }\r\n\r\n /**\r\n * Create navigation link handler\r\n */\r\n createLinkHandler(path: string) {\r\n return (e: MouseEvent) => {\r\n e.preventDefault();\r\n this.navigate(path);\r\n };\r\n }\r\n\r\n /**\r\n * Get underlying router for advanced usage\r\n */\r\n getRouter() {\r\n return this.router;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Unbind event\r\n */\r\n off(target: EventTarget, event: string, handler: EventListener): void {\r\n this.eventsManager.off(target, event, handler);\r\n }\r\n\r\n /**\r\n * Get events manager directly\r\n */\r\n getEventsManager(): EventsManager {\r\n return this.eventsManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get viewport info as reactive signal\r\n */\r\n getViewport() {\r\n return this.windowManager.getViewport();\r\n }\r\n\r\n /**\r\n * Get window manager directly\r\n */\r\n getWindowManager(): WindowManager {\r\n return this.windowManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get i18n instance for translations\r\n */\r\n getI18n() {\r\n return getI18n();\r\n }\r\n\r\n /**\r\n * Get translation string\r\n */\r\n getTranslation(key: string, defaultValue?: string) {\r\n const i18n = getI18n();\r\n if (!i18n) {\r\n console.warn('[ClientManager] i18n not initialized. Using default value or key.');\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get lifecycle phase\r\n */\r\n getPhase() {\r\n return this.lifecycle;\r\n }\r\n\r\n /**\r\n * Check if ready\r\n */\r\n isReady(): boolean {\r\n return this.lifecycle === 'ready';\r\n }\r\n\r\n /**\r\n * Internal logging\r\n */\r\n private log(message: string): void {\r\n if (this.debug) {\r\n console.log(`[ClientManager] ${message}`);\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n// ╔════════════════════════════════════════ UTILS ═════════════════════════════════════════╗\r\n\r\n /**\r\n * Helper to safely get translation\r\n * Use this in components to access translations\r\n */\r\n export function useTranslation() {\r\n const i18n = getI18n();\r\n return {\r\n getTranslation: (key: string, defaultValue?: string) => {\r\n if (!i18n) {\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n },\r\n t: (key: string, defaultValue?: string) => {\r\n if (!i18n) {\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n }\r\n };\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,5 @@
1
+ import * as _minejs_i18n from '@minejs/i18n';
2
+ import { I18nConfig } from '@minejs/i18n';
1
3
  import * as _minejs_browser from '@minejs/browser';
2
4
  import { Router, EventsManager, WindowManager } from '@minejs/browser';
3
5
  import * as _minejs_signals from '@minejs/signals';
@@ -8,6 +10,7 @@ interface ClientManagerConfig {
8
10
  routes: Record<string, RouteComponent>;
9
11
  notFoundComponent?: RouteComponent;
10
12
  debug?: boolean;
13
+ i18n?: I18nConfig;
11
14
  }
12
15
  interface ClientManagerHooks {
13
16
  onBoot?: () => void | Promise<void>;
@@ -46,6 +49,30 @@ declare class ClientManager {
46
49
  * Shutdown the app - Phase 3: DESTROY
47
50
  */
48
51
  destroy(): Promise<void>;
52
+ /**
53
+ * Initializes internationalization (i18n) support
54
+ *
55
+ * Loads language files and configures the i18n system based on:
56
+ * - Default language
57
+ * - Supported languages
58
+ * - Base path for translation files
59
+ * - File extension (json, cjson, etc.)
60
+ *
61
+ * @param {AppConfig} config - Application configuration with i18n settings
62
+ * @param {Logger} logger - Logger instance for logging setup progress
63
+ * @returns {Promise<void>}
64
+ * @throws {Error} If i18n setup or language file loading fails
65
+ *
66
+ * @example
67
+ * await setupI18n({
68
+ * i18n: {
69
+ * defaultLanguage: 'en',
70
+ * supportedLanguages: ['en', 'ar'],
71
+ * basePath: './src/i18n'
72
+ * }
73
+ * }, logger);
74
+ */
75
+ setupI18n(config: I18nConfig): Promise<void>;
49
76
  /**
50
77
  * Navigate to path
51
78
  */
@@ -83,6 +110,14 @@ declare class ClientManager {
83
110
  * Get window manager directly
84
111
  */
85
112
  getWindowManager(): WindowManager;
113
+ /**
114
+ * Get i18n instance for translations
115
+ */
116
+ getI18n(): _minejs_i18n.I18nManager;
117
+ /**
118
+ * Get translation string
119
+ */
120
+ getTranslation(key: string, defaultValue?: string): string;
86
121
  /**
87
122
  * Get lifecycle phase
88
123
  */
@@ -96,5 +131,13 @@ declare class ClientManager {
96
131
  */
97
132
  private log;
98
133
  }
134
+ /**
135
+ * Helper to safely get translation
136
+ * Use this in components to access translations
137
+ */
138
+ declare function useTranslation(): {
139
+ getTranslation: (key: string, defaultValue?: string) => string;
140
+ t: (key: string, defaultValue?: string) => string;
141
+ };
99
142
 
100
- export { ClientManager };
143
+ export { ClientManager, useTranslation };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import * as _minejs_i18n from '@minejs/i18n';
2
+ import { I18nConfig } from '@minejs/i18n';
1
3
  import * as _minejs_browser from '@minejs/browser';
2
4
  import { Router, EventsManager, WindowManager } from '@minejs/browser';
3
5
  import * as _minejs_signals from '@minejs/signals';
@@ -8,6 +10,7 @@ interface ClientManagerConfig {
8
10
  routes: Record<string, RouteComponent>;
9
11
  notFoundComponent?: RouteComponent;
10
12
  debug?: boolean;
13
+ i18n?: I18nConfig;
11
14
  }
12
15
  interface ClientManagerHooks {
13
16
  onBoot?: () => void | Promise<void>;
@@ -46,6 +49,30 @@ declare class ClientManager {
46
49
  * Shutdown the app - Phase 3: DESTROY
47
50
  */
48
51
  destroy(): Promise<void>;
52
+ /**
53
+ * Initializes internationalization (i18n) support
54
+ *
55
+ * Loads language files and configures the i18n system based on:
56
+ * - Default language
57
+ * - Supported languages
58
+ * - Base path for translation files
59
+ * - File extension (json, cjson, etc.)
60
+ *
61
+ * @param {AppConfig} config - Application configuration with i18n settings
62
+ * @param {Logger} logger - Logger instance for logging setup progress
63
+ * @returns {Promise<void>}
64
+ * @throws {Error} If i18n setup or language file loading fails
65
+ *
66
+ * @example
67
+ * await setupI18n({
68
+ * i18n: {
69
+ * defaultLanguage: 'en',
70
+ * supportedLanguages: ['en', 'ar'],
71
+ * basePath: './src/i18n'
72
+ * }
73
+ * }, logger);
74
+ */
75
+ setupI18n(config: I18nConfig): Promise<void>;
49
76
  /**
50
77
  * Navigate to path
51
78
  */
@@ -83,6 +110,14 @@ declare class ClientManager {
83
110
  * Get window manager directly
84
111
  */
85
112
  getWindowManager(): WindowManager;
113
+ /**
114
+ * Get i18n instance for translations
115
+ */
116
+ getI18n(): _minejs_i18n.I18nManager;
117
+ /**
118
+ * Get translation string
119
+ */
120
+ getTranslation(key: string, defaultValue?: string): string;
86
121
  /**
87
122
  * Get lifecycle phase
88
123
  */
@@ -96,5 +131,13 @@ declare class ClientManager {
96
131
  */
97
132
  private log;
98
133
  }
134
+ /**
135
+ * Helper to safely get translation
136
+ * Use this in components to access translations
137
+ */
138
+ declare function useTranslation(): {
139
+ getTranslation: (key: string, defaultValue?: string) => string;
140
+ t: (key: string, defaultValue?: string) => string;
141
+ };
99
142
 
100
- export { ClientManager };
143
+ export { ClientManager, useTranslation };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {signal,effect}from'@minejs/signals';import {EventsManager,WindowManager,createRouter}from'@minejs/browser';var i=class{constructor(e){this.lifecycle="booting";this.hooks={};this.routeComponents={};this.currentPathSignal=signal(window.location.pathname??"/");this.config=e,this.debug=e.debug??false,this.log("[INIT] Creating ClientManager"),this.routeComponents=e.routes,this.eventsManager=new EventsManager,this.windowManager=new WindowManager;let t=Object.entries(e.routes).map(([o,n])=>({path:o,component:n}));this.router=createRouter({routes:t,notFoundComponent:e.notFoundComponent}),this.router.afterEach(o=>{this.currentPathSignal.set(o.path);}),this.log("[INIT] ClientManager created");}on(e,t,o,n){return typeof e=="string"&&e.startsWith("on")?(this.hooks[e]=t,this):this.eventsManager.on(e,t,o,n)}async boot(){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Already booted or destroyed");return}this.log("\u26A1 Phase: BOOT");try{this.hooks.onBoot&&(this.log("\u2192 Calling onBoot hook"),await this.hooks.onBoot()),this.log("\u2713 BOOT phase complete");}catch(e){throw console.error("[ClientManager] Boot failed:",e),e}}async ready(e){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Cannot ready - not in booting phase");return}this.log("\u26A1 Phase: READY");try{this.mount(e),this.log("\u2192 Router mounted"),this.debug&&(globalThis.__CLIENT_MANAGER__=this,this.log("\u2192 ClientManager available globally")),this.hooks.onReady&&(this.log("\u2192 Calling onReady hook"),await this.hooks.onReady()),this.lifecycle="ready",this.log("\u2713 READY phase complete"),this.log("\u2713 App is ready!");}catch(t){throw console.error("[ClientManager] Ready failed:",t),t}}async destroy(){if(this.lifecycle==="destroyed"){console.warn("[ClientManager] Already destroyed");return}this.lifecycle="destroying",this.log("\u26A1 Phase: DESTROY");try{this.hooks.onDestroy&&(this.log("\u2192 Calling onDestroy hook"),await this.hooks.onDestroy()),this.eventsManager.destroy(),this.windowManager.destroy(),this.lifecycle="destroyed",this.log("\u2713 DESTROY phase complete");}catch(e){throw console.error("[ClientManager] Destroy failed:",e),e}}navigate(e){this.router.push(e);}mount(e){let t=typeof e=="string"?document.querySelector(e):e;if(!t){console.warn("[ClientManager] Mount target not found:",e);return}effect(()=>{let o=this.currentPathSignal(),n=this.routeComponents[o]||this.config.notFoundComponent||null;if(t.innerHTML="",n){let r=n();r instanceof Node&&t.appendChild(r);}else t.innerHTML="<p>No component found for this route</p>";this.log(`\u2192 Route changed to: ${o}`);}),this.log("\u2192 Routing setup complete");}getCurrentPath(){return this.currentPathSignal}createLinkHandler(e){return t=>{t.preventDefault(),this.navigate(e);}}getRouter(){return this.router}off(e,t,o){this.eventsManager.off(e,t,o);}getEventsManager(){return this.eventsManager}getViewport(){return this.windowManager.getViewport()}getWindowManager(){return this.windowManager}getPhase(){return this.lifecycle}isReady(){return this.lifecycle==="ready"}log(e){this.debug&&console.log(`[ClientManager] ${e}`);}};export{i as ClientManager};//# sourceMappingURL=index.js.map
1
+ import {signal,effect}from'@minejs/signals';import {setupAuto,getI18n}from'@minejs/i18n';import {EventsManager,WindowManager,createRouter}from'@minejs/browser';var a=class{constructor(t){this.lifecycle="booting";this.hooks={};this.routeComponents={};this.currentPathSignal=signal(window.location.pathname??"/");this.config=t,this.debug=t.debug??false,this.log("[INIT] Creating ClientManager"),this.routeComponents=t.routes,this.eventsManager=new EventsManager,this.windowManager=new WindowManager;let e=Object.entries(t.routes).map(([n,o])=>({path:n,component:o}));this.router=createRouter({routes:e,notFoundComponent:t.notFoundComponent}),this.router.afterEach(n=>{this.currentPathSignal.set(n.path);}),this.log("[INIT] ClientManager created");}on(t,e,n,o){return typeof t=="string"&&t.startsWith("on")?(this.hooks[t]=e,this):this.eventsManager.on(t,e,n,o)}async boot(){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Already booted or destroyed");return}this.log("\u26A1 Phase: BOOT");try{this.config.i18n&&await this.setupI18n(this.config.i18n),this.hooks.onBoot&&(this.log("\u2192 Calling onBoot hook"),await this.hooks.onBoot()),this.log("\u2713 BOOT phase complete");}catch(t){throw console.error("[ClientManager] Boot failed:",t),t}}async ready(t){if(this.lifecycle!=="booting"){console.warn("[ClientManager] Cannot ready - not in booting phase");return}this.log("\u26A1 Phase: READY");try{this.mount(t),this.log("\u2192 Router mounted"),this.debug&&(globalThis.__CLIENT_MANAGER__=this,this.log("\u2192 ClientManager available globally")),this.hooks.onReady&&(this.log("\u2192 Calling onReady hook"),await this.hooks.onReady()),this.lifecycle="ready",this.log("\u2713 READY phase complete"),this.log("\u2713 App is ready!");}catch(e){throw console.error("[ClientManager] Ready failed:",e),e}}async destroy(){if(this.lifecycle==="destroyed"){console.warn("[ClientManager] Already destroyed");return}this.lifecycle="destroying",this.log("\u26A1 Phase: DESTROY");try{this.hooks.onDestroy&&(this.log("\u2192 Calling onDestroy hook"),await this.hooks.onDestroy()),this.eventsManager.destroy(),this.windowManager.destroy(),this.lifecycle="destroyed",this.log("\u2713 DESTROY phase complete");}catch(t){throw console.error("[ClientManager] Destroy failed:",t),t}}async setupI18n(t){this.log("Setting up i18n...");try{await setupAuto({defaultLanguage:t.defaultLanguage,supportedLanguages:t.supportedLanguages,basePath:t.basePath,fileExtension:t.fileExtension||"json"}),this.log(`i18n ready \u2192 ${t.supportedLanguages.join(", ")}`);}catch(e){throw this.log("Failed to setup i18n"+e.message),e}}navigate(t){this.router.push(t);}mount(t){let e=typeof t=="string"?document.querySelector(t):t;if(!e){console.warn("[ClientManager] Mount target not found:",t);return}effect(()=>{let n=this.currentPathSignal(),o=this.routeComponents[n]||this.config.notFoundComponent||null;if(e.innerHTML="",o){let s=o();s instanceof Node&&e.appendChild(s);}else e.innerHTML="<p>No component found for this route</p>";this.log(`\u2192 Route changed to: ${n}`);}),this.log("\u2192 Routing setup complete");}getCurrentPath(){return this.currentPathSignal}createLinkHandler(t){return e=>{e.preventDefault(),this.navigate(t);}}getRouter(){return this.router}off(t,e,n){this.eventsManager.off(t,e,n);}getEventsManager(){return this.eventsManager}getViewport(){return this.windowManager.getViewport()}getWindowManager(){return this.windowManager}getI18n(){return getI18n()}getTranslation(t,e){let n=getI18n();return n?n.t(t)??e??t:(console.warn("[ClientManager] i18n not initialized. Using default value or key."),e??t)}getPhase(){return this.lifecycle}isReady(){return this.lifecycle==="ready"}log(t){this.debug&&console.log(`[ClientManager] ${t}`);}};function C(){let r=getI18n();return {getTranslation:(t,e)=>r?r.t(t)??e??t:e??t,t:(t,e)=>r?r.t(t)??e??t:e??t}}export{a as ClientManager,C as useTranslation};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["ClientManager","config","signal","EventsManager","WindowManager","routesArray","path","component","createRouter","to","eventOrTarget","callbackOrEvent","handler","options","err","mountSelector","selector","container","effect","currentPath","Component","node","e","target","event","message"],"mappings":"uHAkBiBA,CAAAA,CAAN,KAAoB,CAcnB,WAAA,CAAYC,CAAAA,CAAmC,CAP/C,IAAA,CAAQ,SAAA,CAAyE,SAAA,CAEjF,IAAA,CAAQ,KAAA,CAAiD,EAAC,CAE1D,IAAA,CAAQ,gBAA6D,EAAC,CACtE,IAAA,CAAQ,iBAAA,CAAsBC,MAAAA,CAAe,MAAA,CAAO,QAAA,CAAS,QAAA,EAAY,GAAG,CAAA,CAGxE,IAAA,CAAK,MAAA,CAASD,CAAAA,CACd,IAAA,CAAK,KAAA,CAAQA,CAAAA,CAAO,KAAA,EAAS,KAAA,CAE7B,IAAA,CAAK,GAAA,CAAI,+BAA+B,CAAA,CAGxC,IAAA,CAAK,eAAA,CAAkBA,CAAAA,CAAO,MAAA,CAG9B,IAAA,CAAK,aAAA,CAAgB,IAAIE,aAAAA,CACzB,KAAK,aAAA,CAAgB,IAAIC,aAAAA,CAGzB,IAAMC,CAAAA,CAAc,MAAA,CAAO,OAAA,CAAQJ,CAAAA,CAAO,MAAM,CAAA,CAAE,GAAA,CAAI,CAAC,CAACK,CAAAA,CAAMC,CAAS,CAAA,IAAO,CAC1E,IAAA,CAAAD,CAAAA,CACA,SAAA,CAAAC,CACJ,CAAA,CAAE,CAAA,CAEF,IAAA,CAAK,MAAA,CAASC,YAAAA,CAAa,CACvB,MAAA,CAAQH,CAAAA,CACR,iBAAA,CAAmBJ,EAAO,iBAC9B,CAAC,CAAA,CAGD,IAAA,CAAK,MAAA,CAAO,SAAA,CAAWQ,CAAAA,EAAO,CAC1B,IAAA,CAAK,iBAAA,CAAkB,GAAA,CAAIA,CAAAA,CAAG,IAAI,EACtC,CAAC,CAAA,CAED,IAAA,CAAK,GAAA,CAAI,8BAA8B,EAC3C,CAmBA,EAAA,CACIC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACmB,CAEnB,OAAI,OAAOH,CAAAA,EAAkB,UAAYA,CAAAA,CAAc,UAAA,CAAW,IAAI,CAAA,EAClE,IAAA,CAAK,KAAA,CAAMA,CAA+C,CAAA,CAAIC,CAAAA,CACvD,IAAA,EAIJ,IAAA,CAAK,aAAA,CAAc,EAAA,CACtBD,CAAAA,CACAC,EACAC,CAAAA,CACAC,CACJ,CACJ,CAUA,MAAM,IAAA,EAAsB,CACxB,GAAI,IAAA,CAAK,SAAA,GAAc,SAAA,CAAW,CAC9B,OAAA,CAAQ,IAAA,CAAK,6CAA6C,CAAA,CAC1D,MACJ,CAEA,IAAA,CAAK,GAAA,CAAI,oBAAe,CAAA,CAExB,GAAI,CAEI,IAAA,CAAK,KAAA,CAAM,MAAA,GACX,IAAA,CAAK,GAAA,CAAI,4BAAuB,CAAA,CAChC,MAAM,IAAA,CAAK,KAAA,CAAM,MAAA,EAAO,CAAA,CAG5B,IAAA,CAAK,GAAA,CAAI,4BAAuB,EACpC,CAAA,MAASC,CAAAA,CAAK,CACV,MAAA,OAAA,CAAQ,KAAA,CAAM,+BAAgCA,CAAG,CAAA,CAC3CA,CACV,CACJ,CAMA,MAAM,KAAA,CAAMC,CAAAA,CAAoD,CAC5D,GAAI,IAAA,CAAK,SAAA,GAAc,SAAA,CAAW,CAC9B,OAAA,CAAQ,IAAA,CAAK,qDAAqD,CAAA,CAClE,MACJ,CAEA,IAAA,CAAK,GAAA,CAAI,qBAAgB,CAAA,CAEzB,GAAI,CAEA,IAAA,CAAK,KAAA,CAAMA,CAAa,CAAA,CACxB,KAAK,GAAA,CAAI,uBAAkB,CAAA,CAGvB,IAAA,CAAK,KAAA,GACJ,UAAA,CAAmB,kBAAA,CAAqB,IAAA,CACzC,IAAA,CAAK,GAAA,CAAI,yCAAoC,CAAA,CAAA,CAI7C,IAAA,CAAK,KAAA,CAAM,OAAA,GACX,IAAA,CAAK,GAAA,CAAI,6BAAwB,CAAA,CACjC,MAAM,IAAA,CAAK,KAAA,CAAM,OAAA,EAAQ,CAAA,CAG7B,IAAA,CAAK,SAAA,CAAY,OAAA,CACjB,IAAA,CAAK,GAAA,CAAI,6BAAwB,EACjC,IAAA,CAAK,GAAA,CAAI,sBAAiB,EAC9B,CAAA,MAASD,CAAAA,CAAK,CACV,MAAA,OAAA,CAAQ,KAAA,CAAM,+BAAA,CAAiCA,CAAG,CAAA,CAC5CA,CACV,CACJ,CAKA,MAAM,OAAA,EAAyB,CAC3B,GAAI,IAAA,CAAK,SAAA,GAAc,WAAA,CAAa,CAChC,OAAA,CAAQ,IAAA,CAAK,mCAAmC,CAAA,CAChD,MACJ,CAEA,KAAK,SAAA,CAAY,YAAA,CACjB,IAAA,CAAK,GAAA,CAAI,uBAAkB,CAAA,CAE3B,GAAI,CAEI,IAAA,CAAK,KAAA,CAAM,SAAA,GACX,IAAA,CAAK,GAAA,CAAI,+BAA0B,CAAA,CACnC,MAAM,IAAA,CAAK,KAAA,CAAM,SAAA,EAAU,CAAA,CAI/B,IAAA,CAAK,aAAA,CAAc,OAAA,EAAQ,CAC3B,IAAA,CAAK,aAAA,CAAc,OAAA,EAAQ,CAE3B,IAAA,CAAK,SAAA,CAAY,YACjB,IAAA,CAAK,GAAA,CAAI,+BAA0B,EACvC,CAAA,MAASA,CAAAA,CAAK,CACV,MAAA,OAAA,CAAQ,KAAA,CAAM,iCAAA,CAAmCA,CAAG,CAAA,CAC9CA,CACV,CACJ,CAUA,QAAA,CAASR,CAAAA,CAAoB,CACzB,IAAA,CAAK,MAAA,CAAO,IAAA,CAAKA,CAAI,EACzB,CAMA,KAAA,CAAMU,CAAAA,CAAsC,CACxC,IAAMC,CAAAA,CAAY,OAAOD,GAAa,QAAA,CAChC,QAAA,CAAS,aAAA,CAAcA,CAAQ,CAAA,CAC/BA,CAAAA,CAEN,GAAI,CAACC,CAAAA,CAAW,CACZ,OAAA,CAAQ,IAAA,CAAK,yCAAA,CAA2CD,CAAQ,CAAA,CAChE,MACJ,CAGAE,MAAAA,CAAO,IAAM,CACT,IAAMC,CAAAA,CAAc,IAAA,CAAK,iBAAA,EAAkB,CACrCC,CAAAA,CAAY,IAAA,CAAK,eAAA,CAAgBD,CAAW,CAAA,EAC3C,KAAK,MAAA,CAAO,iBAAA,EACZ,IAAA,CAKP,GAFAF,CAAAA,CAAU,SAAA,CAAY,EAAA,CAElBG,CAAAA,CAAW,CACX,IAAMC,CAAAA,CAAOD,CAAAA,EAAU,CACnBC,CAAAA,YAAgB,MAChBJ,CAAAA,CAAU,WAAA,CAAYI,CAAI,EAElC,CAAA,KACIJ,CAAAA,CAAU,SAAA,CAAY,0CAAA,CAG1B,IAAA,CAAK,GAAA,CAAI,CAAA,yBAAA,EAAuBE,CAAW,CAAA,CAAE,EACjD,CAAC,CAAA,CAED,IAAA,CAAK,GAAA,CAAI,+BAA0B,EACvC,CAKA,cAAA,EAAiB,CACb,OAAO,IAAA,CAAK,iBAChB,CAKA,iBAAA,CAAkBb,CAAAA,CAAc,CAC5B,OAAQgB,CAAAA,EAAkB,CACtBA,CAAAA,CAAE,cAAA,EAAe,CACjB,IAAA,CAAK,QAAA,CAAShB,CAAI,EACtB,CACJ,CAKA,SAAA,EAAY,CACR,OAAO,KAAK,MAChB,CAUA,GAAA,CAAIiB,CAAAA,CAAqBC,CAAAA,CAAeZ,CAAAA,CAA8B,CAClE,IAAA,CAAK,aAAA,CAAc,GAAA,CAAIW,CAAAA,CAAQC,CAAAA,CAAOZ,CAAO,EACjD,CAKA,gBAAA,EAAkC,CAC9B,OAAO,IAAA,CAAK,aAChB,CAUA,WAAA,EAAc,CACV,OAAO,IAAA,CAAK,aAAA,CAAc,WAAA,EAC9B,CAKA,gBAAA,EAAkC,CAC9B,OAAO,IAAA,CAAK,aAChB,CAUA,QAAA,EAAW,CACP,OAAO,IAAA,CAAK,SAChB,CAKA,OAAA,EAAmB,CACf,OAAO,IAAA,CAAK,SAAA,GAAc,OAC9B,CAKQ,GAAA,CAAIa,CAAAA,CAAuB,CAC3B,IAAA,CAAK,KAAA,EACL,OAAA,CAAQ,GAAA,CAAI,CAAA,gBAAA,EAAmBA,CAAO,CAAA,CAAE,EAEhD,CAIR","file":"index.js","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { signal, effect } from '@minejs/signals';\r\n import { EventsManager, Router, WindowManager, createRouter } from '@minejs/browser';\r\n import * as types from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n export class ClientManager {\r\n\r\n // ┌──────────────────────────────── INIT ──────────────────────────────┐\r\n\r\n private router : Router;\r\n private eventsManager : EventsManager;\r\n private windowManager : WindowManager;\r\n private lifecycle : 'booting' | 'ready' | 'destroying' | 'destroyed' = 'booting';\r\n private config : types.ClientManagerConfig;\r\n private hooks : types.ClientManagerHooks = {};\r\n private debug : boolean;\r\n private routeComponents : Record<string, types.RouteComponent> = {};\r\n private currentPathSignal = signal<string>(window.location.pathname ?? '/');\r\n\r\n constructor(config: types.ClientManagerConfig) {\r\n this.config = config;\r\n this.debug = config.debug ?? false;\r\n\r\n this.log('[INIT] Creating ClientManager');\r\n\r\n // Store route components provided by user\r\n this.routeComponents = config.routes;\r\n\r\n // Initialize managers from @minejs/browser\r\n this.eventsManager = new EventsManager();\r\n this.windowManager = new WindowManager();\r\n\r\n // Initialize router with user-provided routes\r\n const routesArray = Object.entries(config.routes).map(([path, component]) => ({\r\n path,\r\n component\r\n }));\r\n\r\n this.router = createRouter({\r\n routes: routesArray,\r\n notFoundComponent: config.notFoundComponent,\r\n });\r\n\r\n // Connect router changes to signal for automatic re-rendering\r\n this.router.afterEach((to) => {\r\n this.currentPathSignal.set(to.path);\r\n });\r\n\r\n this.log('[INIT] ClientManager created');\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Setup lifecycle hooks OR bind events\r\n * Overloaded: on(event: 'onBoot'|'onReady'|'onDestroy', callback) - lifecycle\r\n * on(target, event, handler) - event binding\r\n */\r\n on(event: keyof types.ClientManagerHooks, callback: any): this;\r\n on<K extends keyof HTMLElementEventMap>(\r\n target: EventTarget,\r\n event: K | string,\r\n handler: EventListener,\r\n options?: AddEventListenerOptions\r\n ): () => void;\r\n on(\r\n eventOrTarget: keyof types.ClientManagerHooks | EventTarget,\r\n callbackOrEvent?: any,\r\n handler?: EventListener,\r\n options?: AddEventListenerOptions\r\n ): this | (() => void) {\r\n // Check if this is a lifecycle hook call\r\n if (typeof eventOrTarget === 'string' && eventOrTarget.startsWith('on')) {\r\n this.hooks[eventOrTarget as keyof types.ClientManagerHooks] = callbackOrEvent;\r\n return this;\r\n }\r\n\r\n // Otherwise it's an event binding\r\n return this.eventsManager.on(\r\n eventOrTarget as EventTarget,\r\n callbackOrEvent as string,\r\n handler as EventListener,\r\n options\r\n );\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Bootstrap the app - Phase 1: BOOT\r\n */\r\n async boot(): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Already booted or destroyed');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: BOOT');\r\n\r\n try {\r\n // Call user onBoot hook\r\n if (this.hooks.onBoot) {\r\n this.log('→ Calling onBoot hook');\r\n await this.hooks.onBoot();\r\n }\r\n\r\n this.log('✓ BOOT phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Boot failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Ready the app - Phase 2: READY\r\n * Mount to DOM and make everything live\r\n */\r\n async ready(mountSelector: string | HTMLElement): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Cannot ready - not in booting phase');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: READY');\r\n\r\n try {\r\n // Mount router\r\n this.mount(mountSelector);\r\n this.log('→ Router mounted');\r\n\r\n // Setup global access for debugging\r\n if (this.debug) {\r\n (globalThis as any).__CLIENT_MANAGER__ = this;\r\n this.log('→ ClientManager available globally');\r\n }\r\n\r\n // Call user onReady hook\r\n if (this.hooks.onReady) {\r\n this.log('→ Calling onReady hook');\r\n await this.hooks.onReady();\r\n }\r\n\r\n this.lifecycle = 'ready';\r\n this.log('✓ READY phase complete');\r\n this.log('✓ App is ready!');\r\n } catch (err) {\r\n console.error('[ClientManager] Ready failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Shutdown the app - Phase 3: DESTROY\r\n */\r\n async destroy(): Promise<void> {\r\n if (this.lifecycle === 'destroyed') {\r\n console.warn('[ClientManager] Already destroyed');\r\n return;\r\n }\r\n\r\n this.lifecycle = 'destroying';\r\n this.log('⚡ Phase: DESTROY');\r\n\r\n try {\r\n // Call user onDestroy hook\r\n if (this.hooks.onDestroy) {\r\n this.log('→ Calling onDestroy hook');\r\n await this.hooks.onDestroy();\r\n }\r\n\r\n // Cleanup managers\r\n this.eventsManager.destroy();\r\n this.windowManager.destroy();\r\n\r\n this.lifecycle = 'destroyed';\r\n this.log('✓ DESTROY phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Destroy failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Navigate to path\r\n */\r\n navigate(path: string): void {\r\n this.router.push(path);\r\n }\r\n\r\n /**\r\n * Mount router to DOM element and setup reactive routing\r\n * Automatically re-renders when route changes\r\n */\r\n mount(selector: string | HTMLElement): void {\r\n const container = typeof selector === 'string'\r\n ? document.querySelector(selector)\r\n : selector;\r\n\r\n if (!container) {\r\n console.warn('[ClientManager] Mount target not found:', selector);\r\n return;\r\n }\r\n\r\n // Setup reactive routing effect - re-renders when currentPathSignal changes\r\n effect(() => {\r\n const currentPath = this.currentPathSignal();\r\n const Component = this.routeComponents[currentPath]\r\n || this.config.notFoundComponent\r\n || null;\r\n\r\n // Clear and mount new component\r\n container.innerHTML = '';\r\n\r\n if (Component) {\r\n const node = Component();\r\n if (node instanceof Node) {\r\n container.appendChild(node);\r\n }\r\n } else {\r\n container.innerHTML = '<p>No component found for this route</p>';\r\n }\r\n\r\n this.log(`→ Route changed to: ${currentPath}`);\r\n });\r\n\r\n this.log('→ Routing setup complete');\r\n }\r\n\r\n /**\r\n * Get current path signal for reactivity\r\n */\r\n getCurrentPath() {\r\n return this.currentPathSignal;\r\n }\r\n\r\n /**\r\n * Create navigation link handler\r\n */\r\n createLinkHandler(path: string) {\r\n return (e: MouseEvent) => {\r\n e.preventDefault();\r\n this.navigate(path);\r\n };\r\n }\r\n\r\n /**\r\n * Get underlying router for advanced usage\r\n */\r\n getRouter() {\r\n return this.router;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Unbind event\r\n */\r\n off(target: EventTarget, event: string, handler: EventListener): void {\r\n this.eventsManager.off(target, event, handler);\r\n }\r\n\r\n /**\r\n * Get events manager directly\r\n */\r\n getEventsManager(): EventsManager {\r\n return this.eventsManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get viewport info as reactive signal\r\n */\r\n getViewport() {\r\n return this.windowManager.getViewport();\r\n }\r\n\r\n /**\r\n * Get window manager directly\r\n */\r\n getWindowManager(): WindowManager {\r\n return this.windowManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get lifecycle phase\r\n */\r\n getPhase() {\r\n return this.lifecycle;\r\n }\r\n\r\n /**\r\n * Check if ready\r\n */\r\n isReady(): boolean {\r\n return this.lifecycle === 'ready';\r\n }\r\n\r\n /**\r\n * Internal logging\r\n */\r\n private log(message: string): void {\r\n if (this.debug) {\r\n console.log(`[ClientManager] ${message}`);\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["ClientManager","config","signal","EventsManager","WindowManager","routesArray","path","component","createRouter","to","eventOrTarget","callbackOrEvent","handler","options","err","mountSelector","setupAuto","selector","container","effect","currentPath","Component","node","target","event","getI18n","key","defaultValue","i18n","message","useTranslation"],"mappings":"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","file":"index.js","sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\r\n// src/index.ts\r\n//\r\n// Made with ❤️ by Maysara.\r\n\r\n\r\n// ╔════════════════════════════════════════ PACK ════════════════════════════════════════╗\r\n\r\n import { signal, effect } from '@minejs/signals';\r\n import { I18nConfig, setupAuto, getI18n } from '@minejs/i18n';\r\n import { EventsManager, Router, WindowManager, createRouter } from '@minejs/browser';\r\n import * as types from './types';\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n\r\n// ╔════════════════════════════════════════ CORE ════════════════════════════════════════╗\r\n\r\n export class ClientManager {\r\n\r\n // ┌──────────────────────────────── INIT ──────────────────────────────┐\r\n\r\n private router : Router;\r\n private eventsManager : EventsManager;\r\n private windowManager : WindowManager;\r\n private lifecycle : 'booting' | 'ready' | 'destroying' | 'destroyed' = 'booting';\r\n private config : types.ClientManagerConfig;\r\n private hooks : types.ClientManagerHooks = {};\r\n private debug : boolean;\r\n private routeComponents : Record<string, types.RouteComponent> = {};\r\n private currentPathSignal = signal<string>(window.location.pathname ?? '/');\r\n\r\n constructor(config: types.ClientManagerConfig) {\r\n this.config = config;\r\n this.debug = config.debug ?? false;\r\n\r\n this.log('[INIT] Creating ClientManager');\r\n\r\n // Store route components provided by user\r\n this.routeComponents = config.routes;\r\n\r\n // Initialize managers from @minejs/browser\r\n this.eventsManager = new EventsManager();\r\n this.windowManager = new WindowManager();\r\n\r\n // Initialize router with user-provided routes\r\n const routesArray = Object.entries(config.routes).map(([path, component]) => ({\r\n path,\r\n component\r\n }));\r\n\r\n this.router = createRouter({\r\n routes: routesArray,\r\n notFoundComponent: config.notFoundComponent,\r\n });\r\n\r\n // Connect router changes to signal for automatic re-rendering\r\n this.router.afterEach((to) => {\r\n this.currentPathSignal.set(to.path);\r\n });\r\n\r\n this.log('[INIT] ClientManager created');\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Setup lifecycle hooks OR bind events\r\n * Overloaded: on(event: 'onBoot'|'onReady'|'onDestroy', callback) - lifecycle\r\n * on(target, event, handler) - event binding\r\n */\r\n on(event: keyof types.ClientManagerHooks, callback: any): this;\r\n on<K extends keyof HTMLElementEventMap>(\r\n target: EventTarget,\r\n event: K | string,\r\n handler: EventListener,\r\n options?: AddEventListenerOptions\r\n ): () => void;\r\n on(\r\n eventOrTarget: keyof types.ClientManagerHooks | EventTarget,\r\n callbackOrEvent?: any,\r\n handler?: EventListener,\r\n options?: AddEventListenerOptions\r\n ): this | (() => void) {\r\n // Check if this is a lifecycle hook call\r\n if (typeof eventOrTarget === 'string' && eventOrTarget.startsWith('on')) {\r\n this.hooks[eventOrTarget as keyof types.ClientManagerHooks] = callbackOrEvent;\r\n return this;\r\n }\r\n\r\n // Otherwise it's an event binding\r\n return this.eventsManager.on(\r\n eventOrTarget as EventTarget,\r\n callbackOrEvent as string,\r\n handler as EventListener,\r\n options\r\n );\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Bootstrap the app - Phase 1: BOOT\r\n */\r\n async boot(): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Already booted or destroyed');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: BOOT');\r\n\r\n try {\r\n // Setup i18n\r\n if (this.config.i18n)\r\n await this.setupI18n(this.config.i18n!);\r\n\r\n // Call user onBoot hook\r\n if (this.hooks.onBoot) {\r\n this.log('→ Calling onBoot hook');\r\n await this.hooks.onBoot();\r\n }\r\n\r\n this.log('✓ BOOT phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Boot failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Ready the app - Phase 2: READY\r\n * Mount to DOM and make everything live\r\n */\r\n async ready(mountSelector: string | HTMLElement): Promise<void> {\r\n if (this.lifecycle !== 'booting') {\r\n console.warn('[ClientManager] Cannot ready - not in booting phase');\r\n return;\r\n }\r\n\r\n this.log('⚡ Phase: READY');\r\n\r\n try {\r\n // Mount router\r\n this.mount(mountSelector);\r\n this.log('→ Router mounted');\r\n\r\n // Setup global access for debugging\r\n if (this.debug) {\r\n (globalThis as any).__CLIENT_MANAGER__ = this;\r\n this.log('→ ClientManager available globally');\r\n }\r\n\r\n // Call user onReady hook\r\n if (this.hooks.onReady) {\r\n this.log('→ Calling onReady hook');\r\n await this.hooks.onReady();\r\n }\r\n\r\n this.lifecycle = 'ready';\r\n this.log('✓ READY phase complete');\r\n this.log('✓ App is ready!');\r\n } catch (err) {\r\n console.error('[ClientManager] Ready failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Shutdown the app - Phase 3: DESTROY\r\n */\r\n async destroy(): Promise<void> {\r\n if (this.lifecycle === 'destroyed') {\r\n console.warn('[ClientManager] Already destroyed');\r\n return;\r\n }\r\n\r\n this.lifecycle = 'destroying';\r\n this.log('⚡ Phase: DESTROY');\r\n\r\n try {\r\n // Call user onDestroy hook\r\n if (this.hooks.onDestroy) {\r\n this.log('→ Calling onDestroy hook');\r\n await this.hooks.onDestroy();\r\n }\r\n\r\n // Cleanup managers\r\n this.eventsManager.destroy();\r\n this.windowManager.destroy();\r\n\r\n this.lifecycle = 'destroyed';\r\n this.log('✓ DESTROY phase complete');\r\n } catch (err) {\r\n console.error('[ClientManager] Destroy failed:', err);\r\n throw err;\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Initializes internationalization (i18n) support\r\n *\r\n * Loads language files and configures the i18n system based on:\r\n * - Default language\r\n * - Supported languages\r\n * - Base path for translation files\r\n * - File extension (json, cjson, etc.)\r\n *\r\n * @param {AppConfig} config - Application configuration with i18n settings\r\n * @param {Logger} logger - Logger instance for logging setup progress\r\n * @returns {Promise<void>}\r\n * @throws {Error} If i18n setup or language file loading fails\r\n *\r\n * @example\r\n * await setupI18n({\r\n * i18n: {\r\n * defaultLanguage: 'en',\r\n * supportedLanguages: ['en', 'ar'],\r\n * basePath: './src/i18n'\r\n * }\r\n * }, logger);\r\n */\r\n async setupI18n(config: I18nConfig) {\r\n this.log('Setting up i18n...');\r\n\r\n try {\r\n await setupAuto({\r\n defaultLanguage: config.defaultLanguage,\r\n supportedLanguages: config.supportedLanguages,\r\n basePath: config.basePath!,\r\n fileExtension: config.fileExtension || 'json'\r\n });\r\n\r\n this.log(`i18n ready → ${config.supportedLanguages!.join(', ')}`);\r\n } catch (err) {\r\n this.log('Failed to setup i18n' + (err as Error).message);\r\n throw err;\r\n }\r\n }\r\n\r\n /**\r\n * Navigate to path\r\n */\r\n navigate(path: string): void {\r\n this.router.push(path);\r\n }\r\n\r\n /**\r\n * Mount router to DOM element and setup reactive routing\r\n * Automatically re-renders when route changes\r\n */\r\n mount(selector: string | HTMLElement): void {\r\n const container = typeof selector === 'string'\r\n ? document.querySelector(selector)\r\n : selector;\r\n\r\n if (!container) {\r\n console.warn('[ClientManager] Mount target not found:', selector);\r\n return;\r\n }\r\n\r\n // Setup reactive routing effect - re-renders when currentPathSignal changes\r\n effect(() => {\r\n const currentPath = this.currentPathSignal();\r\n const Component = this.routeComponents[currentPath]\r\n || this.config.notFoundComponent\r\n || null;\r\n\r\n // Clear and mount new component\r\n container.innerHTML = '';\r\n\r\n if (Component) {\r\n const node = Component();\r\n if (node instanceof Node) {\r\n container.appendChild(node);\r\n }\r\n } else {\r\n container.innerHTML = '<p>No component found for this route</p>';\r\n }\r\n\r\n this.log(`→ Route changed to: ${currentPath}`);\r\n });\r\n\r\n this.log('→ Routing setup complete');\r\n }\r\n\r\n /**\r\n * Get current path signal for reactivity\r\n */\r\n getCurrentPath() {\r\n return this.currentPathSignal;\r\n }\r\n\r\n /**\r\n * Create navigation link handler\r\n */\r\n createLinkHandler(path: string) {\r\n return (e: MouseEvent) => {\r\n e.preventDefault();\r\n this.navigate(path);\r\n };\r\n }\r\n\r\n /**\r\n * Get underlying router for advanced usage\r\n */\r\n getRouter() {\r\n return this.router;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Unbind event\r\n */\r\n off(target: EventTarget, event: string, handler: EventListener): void {\r\n this.eventsManager.off(target, event, handler);\r\n }\r\n\r\n /**\r\n * Get events manager directly\r\n */\r\n getEventsManager(): EventsManager {\r\n return this.eventsManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get viewport info as reactive signal\r\n */\r\n getViewport() {\r\n return this.windowManager.getViewport();\r\n }\r\n\r\n /**\r\n * Get window manager directly\r\n */\r\n getWindowManager(): WindowManager {\r\n return this.windowManager;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get i18n instance for translations\r\n */\r\n getI18n() {\r\n return getI18n();\r\n }\r\n\r\n /**\r\n * Get translation string\r\n */\r\n getTranslation(key: string, defaultValue?: string) {\r\n const i18n = getI18n();\r\n if (!i18n) {\r\n console.warn('[ClientManager] i18n not initialized. Using default value or key.');\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n\r\n // ┌──────────────────────────────── ──── ──────────────────────────────┐\r\n\r\n /**\r\n * Get lifecycle phase\r\n */\r\n getPhase() {\r\n return this.lifecycle;\r\n }\r\n\r\n /**\r\n * Check if ready\r\n */\r\n isReady(): boolean {\r\n return this.lifecycle === 'ready';\r\n }\r\n\r\n /**\r\n * Internal logging\r\n */\r\n private log(message: string): void {\r\n if (this.debug) {\r\n console.log(`[ClientManager] ${message}`);\r\n }\r\n }\r\n\r\n // └────────────────────────────────────────────────────────────────────┘\r\n\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n\r\n\r\n// ╔════════════════════════════════════════ UTILS ═════════════════════════════════════════╗\r\n\r\n /**\r\n * Helper to safely get translation\r\n * Use this in components to access translations\r\n */\r\n export function useTranslation() {\r\n const i18n = getI18n();\r\n return {\r\n getTranslation: (key: string, defaultValue?: string) => {\r\n if (!i18n) {\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n },\r\n t: (key: string, defaultValue?: string) => {\r\n if (!i18n) {\r\n return defaultValue ?? key;\r\n }\r\n return i18n.t(key) ?? defaultValue ?? key;\r\n }\r\n };\r\n }\r\n\r\n// ╚══════════════════════════════════════════════════════════════════════════════════════╝\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cruxjs/client",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Pure management layer for client-side applications. Routing, lifecycle, events, and viewport management without dictating your architecture.",
5
5
  "keywords": ["cruxjs", "client"],
6
6
  "license": "MIT",
@@ -41,6 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@minejs/browser": "^0.0.4",
44
+ "@minejs/i18n": "^0.0.4",
44
45
  "@minejs/signals": "^0.0.6",
45
46
  "@minejsx/render": "^0.0.3"
46
47
  },