@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 +21 -0
- package/README.md +408 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +100 -0
- package/dist/index.d.ts +100 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
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"]}
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|