@cruxjs/client 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Maysara Elshewehy (https://github.com/maysara-elshewehy)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,408 @@
1
+ <!-- ╔══════════════════════════════ BEG ══════════════════════════════╗ -->
2
+
3
+ <br>
4
+ <div align="center">
5
+ <p>
6
+ <img src="./assets/img/logo.png" alt="logo" style="" height="80" />
7
+ </p>
8
+ </div>
9
+
10
+ <div align="center">
11
+ <img src="https://img.shields.io/badge/v-0.0.1-black"/>
12
+ <img src="https://img.shields.io/badge/🔥-@cruxjs-black"/>
13
+ <br>
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" />
17
+ </div>
18
+ <br>
19
+
20
+ <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
21
+
22
+
23
+
24
+ <!-- ╔══════════════════════════════ DOC ══════════════════════════════╗ -->
25
+
26
+ - ## Quick Start 🔥
27
+
28
+ > **_Pure management layer for client-side applications. Routing, lifecycle, events, and viewport management without dictating your architecture._**
29
+
30
+ - ### Setup
31
+
32
+ > install [`hmm`](https://github.com/minejs-org/hmm) first.
33
+
34
+ ```bash
35
+ hmm i @cruxjs/client
36
+ ```
37
+
38
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
39
+
40
+ - ### Usage
41
+
42
+ ```ts
43
+ import { ClientManager } from '@cruxjs/client';
44
+ import type { ClientManagerConfig } from '@cruxjs/client';
45
+ ```
46
+
47
+ - ### 1. Basic Setup
48
+
49
+ > Create your page components:
50
+
51
+ ```typescript
52
+ import { ClientManager } from '@cruxjs/client';
53
+
54
+ // Your components (you build these)
55
+ const HomePage = () => <div>Welcome Home</div>;
56
+ const AboutPage = () => <div>About Us</div>;
57
+ const NotFoundPage = () => <div>404 - Not Found</div>;
58
+
59
+ // Create manager with YOUR routes
60
+ const app = new ClientManager({
61
+ routes: {
62
+ '/': HomePage,
63
+ '/about': AboutPage
64
+ },
65
+ notFoundComponent: NotFoundPage,
66
+ debug: true
67
+ });
68
+
69
+ // Boot and ready
70
+ await app.boot();
71
+ await app.ready('#app');
72
+
73
+ // App is live and routing is reactive
74
+ ```
75
+
76
+ - ### 2. With Lifecycle Hooks
77
+
78
+ ```typescript
79
+ const app = new ClientManager({
80
+ routes: {
81
+ '/': HomePage,
82
+ '/about': AboutPage
83
+ },
84
+ notFoundComponent: NotFoundPage,
85
+ debug: true
86
+ });
87
+
88
+ // Setup hooks before booting
89
+ app.on('onBoot', async () => {
90
+ console.log('App is booting...');
91
+ // Load data, initialize services, etc.
92
+ });
93
+
94
+ app.on('onReady', async () => {
95
+ console.log('App is live!');
96
+ // Track page views, setup analytics
97
+ });
98
+
99
+ app.on('onDestroy', async () => {
100
+ console.log('Cleaning up...');
101
+ // Close connections, cancel requests
102
+ });
103
+
104
+ // Start lifecycle
105
+ await app.boot();
106
+ await app.ready('#app');
107
+
108
+ // Later, shutdown
109
+ await app.destroy();
110
+ ```
111
+
112
+ - ### 3. With Event Binding
113
+
114
+ ```typescript
115
+ const app = new ClientManager({
116
+ routes: { '/': HomePage },
117
+ notFoundComponent: NotFoundPage
118
+ });
119
+
120
+ // Bind events globally
121
+ const unsubscribe = app.on(
122
+ document,
123
+ 'click',
124
+ (event) => console.log('Document clicked', event)
125
+ );
126
+
127
+ // Or bind to specific elements
128
+ app.on(
129
+ document.getElementById('myButton'),
130
+ 'click',
131
+ () => alert('Button clicked')
132
+ );
133
+
134
+ // Unsubscribe when needed
135
+ unsubscribe();
136
+ ```
137
+
138
+ - ### 4. With Navigation
139
+
140
+ ```typescript
141
+ const app = new ClientManager({
142
+ routes: {
143
+ '/': HomePage,
144
+ '/about': AboutPage,
145
+ '/contact': ContactPage
146
+ },
147
+ notFoundComponent: NotFoundPage
148
+ });
149
+
150
+ await app.boot();
151
+ await app.ready('#app');
152
+
153
+ // Navigate programmatically
154
+ app.navigate('/about');
155
+
156
+ // Get current path as reactive signal
157
+ const currentPath = app.getCurrentPath();
158
+ console.log(currentPath()); // '/about'
159
+
160
+ // Create link handlers for anchor tags
161
+ const handleHomeClick = app.createLinkHandler('/');
162
+ ```
163
+
164
+ - ### 5. With Viewport Reactivity
165
+
166
+ ```typescript
167
+ const app = new ClientManager({
168
+ routes: { '/': HomePage },
169
+ notFoundComponent: NotFoundPage
170
+ });
171
+
172
+ await app.boot();
173
+ await app.ready('#app');
174
+
175
+ // Get viewport as reactive signal
176
+ const viewport = app.getViewport();
177
+
178
+ // Subscribe to viewport changes
179
+ import { effect } from '@minejs/signals';
180
+
181
+ effect(() => {
182
+ const { width, height } = viewport();
183
+ console.log(`Viewport: ${width}x${height}`);
184
+ });
185
+ ```
186
+
187
+ <br>
188
+
189
+ - ## API Reference 🔥
190
+
191
+ ### Core Class
192
+
193
+ - #### `ClientManager`
194
+ > Main class for orchestrating client-side routing, lifecycle, and events
195
+
196
+ ```typescript
197
+ new ClientManager(config: ClientManagerConfig)
198
+ ```
199
+
200
+ **Configuration:**
201
+ ```typescript
202
+ interface ClientManagerConfig {
203
+ // Required: Your page components
204
+ routes: Record<string, RouteComponent>
205
+
206
+ // Optional: 404 fallback component
207
+ notFoundComponent?: RouteComponent
208
+
209
+ // Optional: Enable debug logging
210
+ debug?: boolean
211
+ }
212
+
213
+ type RouteComponent = () => JSXElement | null;
214
+ ```
215
+
216
+ ### Lifecycle Methods
217
+
218
+ - #### `boot(): Promise<void>`
219
+ > Phase 1: Bootstrap the application
220
+ > - Calls `onBoot` hook if defined
221
+ > - Sets lifecycle to 'booting'
222
+
223
+ ```typescript
224
+ await app.boot();
225
+ ```
226
+
227
+ - #### `ready(mountSelector: string | HTMLElement): Promise<void>`
228
+ > Phase 2: Mount and activate the application
229
+ > - Mounts router to DOM
230
+ > - Calls `onReady` hook if defined
231
+ > - Sets lifecycle to 'ready'
232
+ > - Enables reactive routing
233
+
234
+ ```typescript
235
+ await app.ready('#app');
236
+ // or
237
+ await app.ready(document.getElementById('app'));
238
+ ```
239
+
240
+ - #### `destroy(): Promise<void>`
241
+ > Phase 3: Shutdown and cleanup
242
+ > - Calls `onDestroy` hook if defined
243
+ > - Cleans up managers
244
+ > - Sets lifecycle to 'destroyed'
245
+
246
+ ```typescript
247
+ await app.destroy();
248
+ ```
249
+
250
+ ### Lifecycle Hooks
251
+
252
+ - #### `on(event: 'onBoot' | 'onReady' | 'onDestroy', callback): this`
253
+ > Register lifecycle hooks
254
+
255
+ ```typescript
256
+ app.on('onBoot', async () => {
257
+ // Initialize services, load data
258
+ });
259
+
260
+ app.on('onReady', async () => {
261
+ // Track page views, start animations
262
+ });
263
+
264
+ app.on('onDestroy', async () => {
265
+ // Cleanup connections
266
+ });
267
+ ```
268
+
269
+ ### Event Binding
270
+
271
+ - #### `on(target, event, handler, options?): () => void`
272
+ > Bind events to DOM elements with automatic cleanup
273
+
274
+ ```typescript
275
+ const unsubscribe = app.on(
276
+ document.getElementById('button'),
277
+ 'click',
278
+ (event) => console.log('Clicked'),
279
+ { once: true }
280
+ );
281
+
282
+ // Unsubscribe when needed
283
+ unsubscribe();
284
+ ```
285
+
286
+ - #### `off(target, event, handler): void`
287
+ > Unbind events
288
+
289
+ ```typescript
290
+ app.off(element, 'click', myHandler);
291
+ ```
292
+
293
+ ### Routing & Navigation
294
+
295
+ - #### `navigate(path: string): void`
296
+ > Navigate to a route
297
+ > - Updates the router
298
+ > - Triggers reactive re-render
299
+ > - Updates current path signal
300
+
301
+ ```typescript
302
+ app.navigate('/about');
303
+ ```
304
+
305
+ - #### `mount(selector: string | HTMLElement): void`
306
+ > Mount router to DOM element
307
+ > - Called automatically by `ready()`
308
+ > - Sets up reactive routing effect
309
+ > - Handles component rendering
310
+
311
+ ```typescript
312
+ app.mount('#app');
313
+ ```
314
+
315
+ - #### `getCurrentPath(): Signal<string>`
316
+ > Get current path as reactive signal
317
+
318
+ ```typescript
319
+ const pathSignal = app.getCurrentPath();
320
+
321
+ import { effect } from '@minejs/signals';
322
+ effect(() => {
323
+ console.log(`Current path: ${pathSignal()}`);
324
+ });
325
+ ```
326
+
327
+ - #### `createLinkHandler(path: string): (e: MouseEvent) => void`
328
+ > Create a navigation handler for links
329
+
330
+ ```typescript
331
+ const goHome = app.createLinkHandler('/');
332
+
333
+ // Use in template/JSX
334
+ <a href="/" onClick={goHome}>Home</a>
335
+ ```
336
+
337
+ - #### `getRouter(): Router`
338
+ > Access the underlying router for advanced usage
339
+
340
+ ```typescript
341
+ const router = app.getRouter();
342
+ // Use @minejs/browser Router API
343
+ ```
344
+
345
+ ### Manager Access
346
+
347
+ - #### `getEventsManager(): EventsManager`
348
+ > Access the events manager directly
349
+
350
+ ```typescript
351
+ const events = app.getEventsManager();
352
+ ```
353
+
354
+ - #### `getWindowManager(): WindowManager`
355
+ > Access the window/viewport manager directly
356
+
357
+ ```typescript
358
+ const window = app.getWindowManager();
359
+ ```
360
+
361
+ - #### `getViewport(): Signal<ViewportInfo>`
362
+ > Get viewport dimensions as reactive signal
363
+
364
+ ```typescript
365
+ const viewport = app.getViewport();
366
+
367
+ effect(() => {
368
+ const { width, height, isMobile } = viewport();
369
+ console.log(`Size: ${width}x${height}, Mobile: ${isMobile}`);
370
+ });
371
+ ```
372
+
373
+ ### State Inspection
374
+
375
+ - #### `getPhase(): 'booting' | 'ready' | 'destroying' | 'destroyed'`
376
+ > Get current lifecycle phase
377
+
378
+ ```typescript
379
+ const phase = app.getPhase();
380
+ if (phase === 'ready') {
381
+ // App is live
382
+ }
383
+ ```
384
+
385
+ - #### `isReady(): boolean`
386
+ > Check if app is in ready phase
387
+
388
+ ```typescript
389
+ if (app.isReady()) {
390
+ // Safe to use all features
391
+ }
392
+ ```
393
+
394
+ <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
395
+
396
+
397
+
398
+ <!-- ╔══════════════════════════════ END ══════════════════════════════╗ -->
399
+
400
+ <br>
401
+
402
+ ---
403
+
404
+ <div align="center">
405
+ <a href="https://github.com/maysara-elshewehy"><img src="https://img.shields.io/badge/by-Maysara-black"/></a>
406
+ </div>
407
+
408
+ <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
package/dist/index.cjs ADDED
@@ -0,0 +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
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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"]}
@@ -0,0 +1,100 @@
1
+ import * as _minejs_browser from '@minejs/browser';
2
+ import { Router, EventsManager, WindowManager } from '@minejs/browser';
3
+ import * as _minejs_signals from '@minejs/signals';
4
+ import { JSXElement } from '@minejsx/render';
5
+
6
+ type RouteComponent = () => JSXElement | null;
7
+ interface ClientManagerConfig {
8
+ routes: Record<string, RouteComponent>;
9
+ notFoundComponent?: RouteComponent;
10
+ debug?: boolean;
11
+ }
12
+ interface ClientManagerHooks {
13
+ onBoot?: () => void | Promise<void>;
14
+ onReady?: () => void | Promise<void>;
15
+ onDestroy?: () => void | Promise<void>;
16
+ }
17
+
18
+ declare class ClientManager {
19
+ private router;
20
+ private eventsManager;
21
+ private windowManager;
22
+ private lifecycle;
23
+ private config;
24
+ private hooks;
25
+ private debug;
26
+ private routeComponents;
27
+ private currentPathSignal;
28
+ constructor(config: ClientManagerConfig);
29
+ /**
30
+ * Setup lifecycle hooks OR bind events
31
+ * Overloaded: on(event: 'onBoot'|'onReady'|'onDestroy', callback) - lifecycle
32
+ * on(target, event, handler) - event binding
33
+ */
34
+ on(event: keyof ClientManagerHooks, callback: any): this;
35
+ on<K extends keyof HTMLElementEventMap>(target: EventTarget, event: K | string, handler: EventListener, options?: AddEventListenerOptions): () => void;
36
+ /**
37
+ * Bootstrap the app - Phase 1: BOOT
38
+ */
39
+ boot(): Promise<void>;
40
+ /**
41
+ * Ready the app - Phase 2: READY
42
+ * Mount to DOM and make everything live
43
+ */
44
+ ready(mountSelector: string | HTMLElement): Promise<void>;
45
+ /**
46
+ * Shutdown the app - Phase 3: DESTROY
47
+ */
48
+ destroy(): Promise<void>;
49
+ /**
50
+ * Navigate to path
51
+ */
52
+ navigate(path: string): void;
53
+ /**
54
+ * Mount router to DOM element and setup reactive routing
55
+ * Automatically re-renders when route changes
56
+ */
57
+ mount(selector: string | HTMLElement): void;
58
+ /**
59
+ * Get current path signal for reactivity
60
+ */
61
+ getCurrentPath(): _minejs_signals.Signal<string>;
62
+ /**
63
+ * Create navigation link handler
64
+ */
65
+ createLinkHandler(path: string): (e: MouseEvent) => void;
66
+ /**
67
+ * Get underlying router for advanced usage
68
+ */
69
+ getRouter(): Router;
70
+ /**
71
+ * Unbind event
72
+ */
73
+ off(target: EventTarget, event: string, handler: EventListener): void;
74
+ /**
75
+ * Get events manager directly
76
+ */
77
+ getEventsManager(): EventsManager;
78
+ /**
79
+ * Get viewport info as reactive signal
80
+ */
81
+ getViewport(): _minejs_signals.Signal<_minejs_browser.ViewportInfo>;
82
+ /**
83
+ * Get window manager directly
84
+ */
85
+ getWindowManager(): WindowManager;
86
+ /**
87
+ * Get lifecycle phase
88
+ */
89
+ getPhase(): "booting" | "ready" | "destroying" | "destroyed";
90
+ /**
91
+ * Check if ready
92
+ */
93
+ isReady(): boolean;
94
+ /**
95
+ * Internal logging
96
+ */
97
+ private log;
98
+ }
99
+
100
+ export { ClientManager };
@@ -0,0 +1,100 @@
1
+ import * as _minejs_browser from '@minejs/browser';
2
+ import { Router, EventsManager, WindowManager } from '@minejs/browser';
3
+ import * as _minejs_signals from '@minejs/signals';
4
+ import { JSXElement } from '@minejsx/render';
5
+
6
+ type RouteComponent = () => JSXElement | null;
7
+ interface ClientManagerConfig {
8
+ routes: Record<string, RouteComponent>;
9
+ notFoundComponent?: RouteComponent;
10
+ debug?: boolean;
11
+ }
12
+ interface ClientManagerHooks {
13
+ onBoot?: () => void | Promise<void>;
14
+ onReady?: () => void | Promise<void>;
15
+ onDestroy?: () => void | Promise<void>;
16
+ }
17
+
18
+ declare class ClientManager {
19
+ private router;
20
+ private eventsManager;
21
+ private windowManager;
22
+ private lifecycle;
23
+ private config;
24
+ private hooks;
25
+ private debug;
26
+ private routeComponents;
27
+ private currentPathSignal;
28
+ constructor(config: ClientManagerConfig);
29
+ /**
30
+ * Setup lifecycle hooks OR bind events
31
+ * Overloaded: on(event: 'onBoot'|'onReady'|'onDestroy', callback) - lifecycle
32
+ * on(target, event, handler) - event binding
33
+ */
34
+ on(event: keyof ClientManagerHooks, callback: any): this;
35
+ on<K extends keyof HTMLElementEventMap>(target: EventTarget, event: K | string, handler: EventListener, options?: AddEventListenerOptions): () => void;
36
+ /**
37
+ * Bootstrap the app - Phase 1: BOOT
38
+ */
39
+ boot(): Promise<void>;
40
+ /**
41
+ * Ready the app - Phase 2: READY
42
+ * Mount to DOM and make everything live
43
+ */
44
+ ready(mountSelector: string | HTMLElement): Promise<void>;
45
+ /**
46
+ * Shutdown the app - Phase 3: DESTROY
47
+ */
48
+ destroy(): Promise<void>;
49
+ /**
50
+ * Navigate to path
51
+ */
52
+ navigate(path: string): void;
53
+ /**
54
+ * Mount router to DOM element and setup reactive routing
55
+ * Automatically re-renders when route changes
56
+ */
57
+ mount(selector: string | HTMLElement): void;
58
+ /**
59
+ * Get current path signal for reactivity
60
+ */
61
+ getCurrentPath(): _minejs_signals.Signal<string>;
62
+ /**
63
+ * Create navigation link handler
64
+ */
65
+ createLinkHandler(path: string): (e: MouseEvent) => void;
66
+ /**
67
+ * Get underlying router for advanced usage
68
+ */
69
+ getRouter(): Router;
70
+ /**
71
+ * Unbind event
72
+ */
73
+ off(target: EventTarget, event: string, handler: EventListener): void;
74
+ /**
75
+ * Get events manager directly
76
+ */
77
+ getEventsManager(): EventsManager;
78
+ /**
79
+ * Get viewport info as reactive signal
80
+ */
81
+ getViewport(): _minejs_signals.Signal<_minejs_browser.ViewportInfo>;
82
+ /**
83
+ * Get window manager directly
84
+ */
85
+ getWindowManager(): WindowManager;
86
+ /**
87
+ * Get lifecycle phase
88
+ */
89
+ getPhase(): "booting" | "ready" | "destroying" | "destroyed";
90
+ /**
91
+ * Check if ready
92
+ */
93
+ isReady(): boolean;
94
+ /**
95
+ * Internal logging
96
+ */
97
+ private log;
98
+ }
99
+
100
+ export { ClientManager };
package/dist/index.js ADDED
@@ -0,0 +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
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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.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"]}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@cruxjs/client",
3
+ "version": "0.0.1",
4
+ "description": "Pure management layer for client-side applications. Routing, lifecycle, events, and viewport management without dictating your architecture.",
5
+ "keywords": ["cruxjs", "client"],
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/cruxjs-org/client#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/cruxjs-org/client/issues"
10
+ },
11
+ "author": {
12
+ "name": "Maysara",
13
+ "email": "maysara.elshewehy@gmail.com",
14
+ "url": "https://github.com/maysara-elshewehy"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/cruxjs-org/client.git"
19
+ },
20
+ "type": "module",
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "files": ["dist"],
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js",
28
+ "require": "./dist/index.js"
29
+ }
30
+ },
31
+ "scripts": {
32
+ "build": "tsup",
33
+ "lint": "eslint src --ext .ts",
34
+ "test": "bun test"
35
+ },
36
+ "engines": {
37
+ "bun": ">=1.3.3"
38
+ },
39
+ "peerDependencies": {
40
+ "bun": "^1.3.3"
41
+ },
42
+ "dependencies": {
43
+ "@minejs/browser": "^0.0.4",
44
+ "@minejs/signals": "^0.0.6",
45
+ "@minejsx/render": "^0.0.3"
46
+ },
47
+ "devDependencies": {
48
+ "@eslint/js": "^9.39.2",
49
+ "@stylistic/eslint-plugin": "^5.6.1",
50
+ "@types/bun": "^1.3.5",
51
+ "@types/node": "^20.19.27",
52
+ "bun-plugin-dts": "^0.3.0",
53
+ "bun-types": "^1.3.5",
54
+ "ts-node": "^10.9.2",
55
+ "tsup": "^8.5.1",
56
+ "typescript": "^5.9.3",
57
+ "typescript-eslint": "^8.52.0"
58
+ }
59
+ }