@devisfuture/electron-modular 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +731 -0
  3. package/dist/@core/bootstrap/bootstrap.d.ts +2 -0
  4. package/dist/@core/bootstrap/bootstrap.js +20 -0
  5. package/dist/@core/bootstrap/initialize-ipc/handlers.d.ts +3 -0
  6. package/dist/@core/bootstrap/initialize-ipc/handlers.js +47 -0
  7. package/dist/@core/bootstrap/initialize-ipc/window-creator.d.ts +3 -0
  8. package/dist/@core/bootstrap/initialize-ipc/window-creator.js +28 -0
  9. package/dist/@core/bootstrap/initialize-ipc/window-event-listeners.d.ts +3 -0
  10. package/dist/@core/bootstrap/initialize-ipc/window-event-listeners.js +61 -0
  11. package/dist/@core/bootstrap/initialize-ipc/window-instance-creator.d.ts +3 -0
  12. package/dist/@core/bootstrap/initialize-ipc/window-instance-creator.js +10 -0
  13. package/dist/@core/bootstrap/initialize-module.d.ts +3 -0
  14. package/dist/@core/bootstrap/initialize-module.js +18 -0
  15. package/dist/@core/bootstrap/instantiate-module.d.ts +2 -0
  16. package/dist/@core/bootstrap/instantiate-module.js +9 -0
  17. package/dist/@core/bootstrap/register-imports.d.ts +2 -0
  18. package/dist/@core/bootstrap/register-imports.js +12 -0
  19. package/dist/@core/bootstrap/register-ipc-handlers.d.ts +3 -0
  20. package/dist/@core/bootstrap/register-ipc-handlers.js +9 -0
  21. package/dist/@core/bootstrap/register-providers.d.ts +3 -0
  22. package/dist/@core/bootstrap/register-providers.js +21 -0
  23. package/dist/@core/bootstrap/register-windows.d.ts +3 -0
  24. package/dist/@core/bootstrap/register-windows.js +15 -0
  25. package/dist/@core/bootstrap/settings.d.ts +11 -0
  26. package/dist/@core/bootstrap/settings.js +13 -0
  27. package/dist/@core/container.d.ts +26 -0
  28. package/dist/@core/container.js +154 -0
  29. package/dist/@core/control-window/cache.d.ts +1 -0
  30. package/dist/@core/control-window/cache.js +1 -0
  31. package/dist/@core/control-window/create.d.ts +3 -0
  32. package/dist/@core/control-window/create.js +63 -0
  33. package/dist/@core/control-window/destroy.d.ts +1 -0
  34. package/dist/@core/control-window/destroy.js +9 -0
  35. package/dist/@core/control-window/receive.d.ts +2 -0
  36. package/dist/@core/control-window/receive.js +8 -0
  37. package/dist/@core/control-window/types.d.ts +14 -0
  38. package/dist/@core/control-window/types.js +1 -0
  39. package/dist/@core/decorators/inject.d.ts +6 -0
  40. package/dist/@core/decorators/inject.js +15 -0
  41. package/dist/@core/decorators/injectable.d.ts +2 -0
  42. package/dist/@core/decorators/injectable.js +6 -0
  43. package/dist/@core/decorators/ipc-handler.d.ts +2 -0
  44. package/dist/@core/decorators/ipc-handler.js +6 -0
  45. package/dist/@core/decorators/rg-module.d.ts +3 -0
  46. package/dist/@core/decorators/rg-module.js +6 -0
  47. package/dist/@core/decorators/window-manager.d.ts +3 -0
  48. package/dist/@core/decorators/window-manager.js +6 -0
  49. package/dist/@core/errors/index.d.ts +19 -0
  50. package/dist/@core/errors/index.js +31 -0
  51. package/dist/@core/types/constructor.d.ts +1 -0
  52. package/dist/@core/types/constructor.js +1 -0
  53. package/dist/@core/types/index.d.ts +7 -0
  54. package/dist/@core/types/index.js +1 -0
  55. package/dist/@core/types/ipc-handler.d.ts +7 -0
  56. package/dist/@core/types/ipc-handler.js +1 -0
  57. package/dist/@core/types/module-metadata.d.ts +10 -0
  58. package/dist/@core/types/module-metadata.js +1 -0
  59. package/dist/@core/types/provider.d.ts +21 -0
  60. package/dist/@core/types/provider.js +1 -0
  61. package/dist/@core/types/window-factory.d.ts +6 -0
  62. package/dist/@core/types/window-factory.js +1 -0
  63. package/dist/@core/types/window-manager.d.ts +10 -0
  64. package/dist/@core/types/window-manager.js +1 -0
  65. package/dist/@core/types/window-metadata.d.ts +6 -0
  66. package/dist/@core/types/window-metadata.js +1 -0
  67. package/dist/@core/utils/dependency-tokens.d.ts +4 -0
  68. package/dist/@core/utils/dependency-tokens.js +23 -0
  69. package/dist/config.d.ts +4 -0
  70. package/dist/config.js +4 -0
  71. package/dist/index.d.ts +26 -0
  72. package/dist/index.js +34 -0
  73. package/package.json +53 -0
package/README.md ADDED
@@ -0,0 +1,731 @@
1
+ # Package Documentation
2
+
3
+ ## Overview
4
+
5
+ A lightweight dependency injection container for Electron's main process that brings modular architecture and clean code organization to your desktop applications.
6
+
7
+ ### What It Solves
8
+
9
+ Building complex Electron apps often leads to tightly coupled code, scattered IPC handlers, and difficulty managing window lifecycles. This package addresses these challenges by providing:
10
+
11
+ - **Organized Code Structure** - Split your application into independent, testable modules instead of monolithic files
12
+ - **Automatic Dependency Management** - No more manual service instantiation or passing dependencies through multiple layers
13
+ - **Centralized IPC Logic** - Group related IPC handlers with their business logic instead of scattering them across your codebase
14
+ - **Window Lifecycle Control** - Manage BrowserWindow creation, caching, and event handling in dedicated classes
15
+ - **Type-Safe Module Boundaries** - Share only necessary interfaces between modules using the provider pattern
16
+
17
+ ### What You Get
18
+
19
+ The package uses TypeScript decorators (`@RgModule`, `@Injectable`, `@IpcHandler`, `@WindowManager`) to eliminate boilerplate and let you focus on business logic. Services are automatically instantiated with their dependencies, IPC handlers are registered during module initialization, and windows are created with lifecycle hooks that run at the right time.
20
+
21
+ Instead of wrestling with service initialization order or managing global state, you define modules with clear dependencies and let the container handle the rest.
22
+
23
+ ### Key Features
24
+
25
+ - **Dependency Injection** - Automatic service instantiation and injection
26
+ - **Module System** - Organize code into feature modules
27
+ - **TypeScript Decorators** - `@RgModule`, `@Injectable`, `@IpcHandler`, `@WindowManager`
28
+ - **Provider Pattern** - Share only necessary interfaces between modules
29
+ - **Type Safety** - Full TypeScript support
30
+
31
+ ---
32
+
33
+ ## Installation
34
+
35
+ Install with your package manager:
36
+
37
+ ```bash
38
+ # npm
39
+ npm install @devisfuture/electron-modular
40
+
41
+ # yarn
42
+ yarn add @devisfuture/electron-modular
43
+
44
+ # pnpm
45
+ pnpm add @devisfuture/electron-modular
46
+ ```
47
+
48
+ Peer dependency:
49
+
50
+ This package targets Electron's main process and declares Electron >=36 as a peer dependency. Ensure Electron is installed in your project:
51
+
52
+ ```bash
53
+ npm install --save-dev electron@^36
54
+ ```
55
+
56
+ TypeScript setup:
57
+
58
+ - Enable decorators and metadata in your `tsconfig.json`:
59
+
60
+ ```json
61
+ "experimentalDecorators": true,
62
+ "emitDecoratorMetadata": true
63
+ ```
64
+
65
+ > Tip: This package is published as ESM. When importing local modules, use `.js` extensions in runtime imports, e.g. `import { UserModule } from "./user/module.js"`.
66
+
67
+ ---
68
+
69
+ ## Quick Start
70
+
71
+ ### 1. Bootstrap Application `main.ts`
72
+
73
+ Initialize the framework and bootstrap all modules:
74
+
75
+ ```typescript
76
+ import { app } from "electron";
77
+ import { initSettings, bootstrapModules } from "@devisfuture/electron-modular";
78
+ import { UserModule } from "./user/module.js";
79
+ import { ResourcesModule } from "./resources/module.js";
80
+
81
+ initSettings({
82
+ baseRestApi: process.env.BASE_REST_API ?? "",
83
+ localhostPort: process.env.LOCALHOST_ELECTRON_SERVER_PORT ?? "",
84
+ folders: {
85
+ distRenderer: "dist-renderer",
86
+ distMain: "dist-main",
87
+ },
88
+ });
89
+
90
+ app.on("ready", async () => {
91
+ await bootstrapModules([
92
+ UserModule,
93
+ ResourcesModule,
94
+ // ... other modules
95
+ ]);
96
+ });
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Module Structure
102
+
103
+ An example of each module's structure, but you can use your own:
104
+
105
+ ```
106
+ user/
107
+ ├── module.ts # Module definition
108
+ ├── service.ts # Business logic or several services in the folder
109
+ ├── ipc.ts # IPC handlers (optional) or several ipc in the folder
110
+ ├── window.ts # Window manager (optional) or several windows in the folder
111
+ ├── tokens.ts # DI tokens (optional)
112
+ └── types.ts # Type definitions (optional)
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Two Approaches to Using Modules
118
+
119
+ ### Approach 1: Direct Service Injection (Simple)
120
+
121
+ Import a module and directly inject its exported service.
122
+
123
+ #### Module Definition `user/module.ts`
124
+
125
+ ```typescript
126
+ import { RgModule } from "@devisfuture/electron-modular";
127
+ import { RestApiModule } from "../rest-api/module.js";
128
+ import { UserService } from "./service.js";
129
+ import { UserIpc } from "./ipc.js";
130
+
131
+ @RgModule({
132
+ imports: [RestApiModule],
133
+ ipc: [UserIpc],
134
+ providers: [UserService],
135
+ exports: [UserService],
136
+ })
137
+ export class UserModule {}
138
+ ```
139
+
140
+ #### Service Implementation `user/service.ts`
141
+
142
+ ```typescript
143
+ import { Injectable } from "@devisfuture/electron-modular";
144
+ import { RestApiService } from "../rest-api/service.js";
145
+
146
+ @Injectable()
147
+ export class UserService {
148
+ constructor(private restApiService: RestApiService) {}
149
+
150
+ async byId<R extends TUser>(id: string): Promise<R | undefined> {
151
+ const response = await this.restApiService.get<R>(
152
+ `https://example.com/api/users/${id}`,
153
+ );
154
+
155
+ if (response.error !== undefined) {
156
+ return;
157
+ }
158
+
159
+ return response.data;
160
+ }
161
+ }
162
+ ```
163
+
164
+ **When to use:**
165
+
166
+ - Simple dependencies
167
+ - You need the full service functionality
168
+ - No circular dependency issues
169
+
170
+ ---
171
+
172
+ ### Approach 2: Provider Pattern (Advanced)
173
+
174
+ Use `provide` and `useFactory` to expose only necessary interface.
175
+
176
+ #### tokens.ts
177
+
178
+ ```typescript
179
+ export const USER_REST_API_PROVIDER = Symbol("USER_REST_API_PROVIDER");
180
+ ```
181
+
182
+ #### types.ts
183
+
184
+ ```typescript
185
+ export type TUserRestApiProvider = {
186
+ get: <T>(
187
+ endpoint: string,
188
+ options?: AxiosRequestConfig,
189
+ ) => Promise<TResponse<T>>;
190
+ post: <T>(
191
+ endpoint: string,
192
+ data: unknown,
193
+ options?: AxiosRequestConfig,
194
+ ) => Promise<TResponse<T>>;
195
+ };
196
+ ```
197
+
198
+ #### Module Definition `user/module.ts`
199
+
200
+ ```typescript
201
+ import { RgModule } from "@devisfuture/electron-modular";
202
+ import { RestApiModule } from "../rest-api/module.js";
203
+ import { RestApiService } from "../rest-api/service.js";
204
+ import { UserService } from "./service.js";
205
+ import { UserIpc } from "./ipc.js";
206
+ import { USER_REST_API_PROVIDER } from "./tokens.js";
207
+ import type { TUserRestApiProvider } from "./types.js";
208
+
209
+ @RgModule({
210
+ imports: [RestApiModule],
211
+ ipc: [UserIpc],
212
+ providers: [
213
+ UserService,
214
+ {
215
+ provide: USER_REST_API_PROVIDER,
216
+ useFactory: (restApiService: RestApiService): TUserRestApiProvider => ({
217
+ get: (endpoint, options) => restApiService.get(endpoint, options),
218
+ post: (endpoint, data, options) =>
219
+ restApiService.post(endpoint, data, options),
220
+ }),
221
+ inject: [RestApiService],
222
+ },
223
+ ],
224
+ exports: [UserService],
225
+ })
226
+ export class UserModule {}
227
+ ```
228
+
229
+ #### Service Implementation `user/service.ts`
230
+
231
+ ```typescript
232
+ @Injectable()
233
+ export class UserService {
234
+ constructor(
235
+ @Inject(USER_REST_API_PROVIDER)
236
+ private restApiProvider: TUserRestApiProvider,
237
+ ) {}
238
+
239
+ async byId<R extends TUser>(id: string): Promise<R | undefined> {
240
+ const response = await this.restApiProvider.get<R>(
241
+ `https://example.com/api/users/${id}`,
242
+ );
243
+
244
+ if (response.error !== undefined) {
245
+ return;
246
+ }
247
+
248
+ return response.data;
249
+ }
250
+ }
251
+ ```
252
+
253
+ **When to use:**
254
+
255
+ - Need to expose limited interface
256
+ - Prevent circular dependencies
257
+ - Multiple implementations possible
258
+ - Better encapsulation
259
+
260
+ ---
261
+
262
+ ## IPC Handlers
263
+
264
+ Handle communication between main and renderer processes.
265
+
266
+ ```typescript
267
+ import {
268
+ IpcHandler,
269
+ TIpcHandlerInterface,
270
+ TParamOnInit,
271
+ } from "@devisfuture/electron-modular";
272
+ import { UserService } from "./service.js";
273
+
274
+ @IpcHandler()
275
+ export class UserIpc implements TIpcHandlerInterface {
276
+ constructor(private userService: UserService) {}
277
+
278
+ async onInit({ getWindow }: TParamOnInit<TWindows["main"]>) {
279
+ const mainWindow = getWindow("window:main");
280
+
281
+ ipcMainOn("user:fetch", async (event, userId: string) => {
282
+ const user = await this.userService.byId(userId);
283
+ event.reply("user:fetch:response", user);
284
+ });
285
+ }
286
+ }
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Window Managers
292
+
293
+ Manage BrowserWindow lifecycle and configuration.
294
+
295
+ ```typescript
296
+ import { WindowManager } from "@devisfuture/electron-modular";
297
+ import type { TWindowManager } from "../types.js";
298
+
299
+ @WindowManager<TWindows["userProfile"]>({
300
+ hash: "window:user-profile",
301
+ isCache: true,
302
+ options: {
303
+ width: 600,
304
+ height: 400,
305
+ resizable: false,
306
+ },
307
+ })
308
+ export class UserWindow implements TWindowManager {
309
+ constructor(private userService: UserService) {}
310
+
311
+ onWebContentsDidFinishLoad(window: BrowserWindow): void {
312
+ // Initialize when window content loads
313
+ this.loadUserData(window);
314
+ }
315
+
316
+ private async loadUserData(window: BrowserWindow): Promise<void> {
317
+ const user = await this.userService.getCurrentUser();
318
+ window.webContents.send("user:loaded", user);
319
+ }
320
+ }
321
+ ```
322
+
323
+ ### Lifecycle Hooks (Window & WebContents events) ✅
324
+
325
+ The window manager supports lifecycle hooks by naming methods on your class following a simple convention:
326
+
327
+ - Use `on<ClassicEvent>` for BrowserWindow events (e.g. `onFocus`, `onMaximize`).
328
+ - Use `onWebContents<Thing>` for WebContents events (e.g. `onWebContentsDidFinishLoad`, `onWebContentsWillNavigate`).
329
+
330
+ How method names map to Electron events:
331
+
332
+ - The framework removes the `on` or `onWebContents` prefix, converts the remaining CamelCase to kebab-case and uses that as the event name.
333
+ - `onFocus` → `focus`
334
+ - `onMaximize` → `maximize`
335
+ - `onWebContentsDidFinishLoad` → `did-finish-load`
336
+ - `onWebContentsWillNavigate` → `will-navigate`
337
+
338
+ Handler signatures and parameters 🔧
339
+
340
+ - If your method declares 0 or 1 parameter (i.e. `handler.length <= 1`) it will be called with the `BrowserWindow` instance only:
341
+
342
+ ```ts
343
+ onFocus(window: BrowserWindow): void {
344
+ // Called when window receives focus
345
+ window.webContents.send("window:focused");
346
+ }
347
+ ```
348
+
349
+ - If your method declares more than 1 parameter, the original Electron event arguments are forwarded first and the `BrowserWindow` is appended as the last argument. This is useful for WebContents or events that include event objects and additional data:
350
+
351
+ ```ts
352
+ onWebContentsWillNavigate(ev: Electron.Event, url: string, window: BrowserWindow) {
353
+ // ev and url come from webContents, window is appended by the framework
354
+ console.log("navigating to", url);
355
+ }
356
+ ```
357
+
358
+ Common BrowserWindow events you can handle:
359
+
360
+ - `onFocus`, `onBlur`, `onMaximize`, `onUnmaximize`, `onMinimize`, `onRestore`, `onResize`, `onMove`, `onClose`, `onClosed`
361
+
362
+ Common WebContents events you can handle:
363
+
364
+ - `onWebContentsDidFinishLoad`, `onWebContentsDidFailLoad`, `onWebContentsDomReady`, `onWebContentsWillNavigate`, `onWebContentsDidNavigate`, `onWebContentsNewWindow`, `onWebContentsDestroyed`
365
+
366
+ Important implementation notes ⚠️
367
+
368
+ - Handlers are attached per BrowserWindow instance and cleaned up automatically when the window is closed, so you don't have to manually remove listeners.
369
+ - The same instance and set of handlers are tracked in a WeakMap internally; re-attaching the same `windowInstance` will not duplicate listeners.
370
+
371
+ ---
372
+
373
+ ## TypeScript types — `TWindows["myWindow"]`
374
+
375
+ `TWindows` maps window keys to their unique hash strings. Use `TWindows["<key>"]` for typing windows in `@WindowManager` and `getWindow`.
376
+
377
+ ```typescript
378
+ // types/windows.d.ts
379
+ type TWindows = {
380
+ main: "window:main";
381
+ updateResource: "window/resource/update";
382
+ };
383
+ ```
384
+
385
+ Examples:
386
+
387
+ ```typescript
388
+ // Using as generic for WindowManager
389
+ @WindowManager<TWindows["main"]>({
390
+ hash: "window:main",
391
+ isCache: true,
392
+ options: {},
393
+ })
394
+ export class AppWindow implements TWindowManager {}
395
+
396
+ // Using with getWindow()
397
+ const mainWindow = getWindow<TWindows["main"]>("window:main");
398
+ ```
399
+
400
+ ---
401
+
402
+ ## API Reference
403
+
404
+ ### Core Decorators
405
+
406
+ #### `@RgModule(config)`
407
+
408
+ Defines a module with its dependencies and providers.
409
+
410
+ **Parameters:**
411
+
412
+ - `imports?: Class[]` - Modules to import
413
+ - `providers?: Provider[]` - Services and factories
414
+ - `ipc?: Class[]` - IPC handler classes
415
+ - `windows?: Class[]` - Window manager classes
416
+ - `exports?: Class[]` - Providers to export
417
+
418
+ #### `@Injectable()`
419
+
420
+ Marks a class as injectable into the DI container.
421
+
422
+ ```typescript
423
+ @Injectable()
424
+ export class MyService {
425
+ constructor(private dependency: OtherService) {}
426
+ }
427
+ ```
428
+
429
+ #### `@Inject(token)`
430
+
431
+ Injects a dependency by token (Symbol).
432
+
433
+ ```typescript
434
+ constructor(
435
+ @Inject(MY_PROVIDER) private provider: TMyProvider
436
+ ) {}
437
+ ```
438
+
439
+ #### `@IpcHandler()`
440
+
441
+ Marks a class as an IPC communication handler.
442
+
443
+ ```typescript
444
+ @IpcHandler()
445
+ export class MyIpc implements TIpcHandlerInterface {
446
+ async onInit({ getWindow }: TParamOnInit) {
447
+ // Setup IPC listeners
448
+ }
449
+ }
450
+ ```
451
+
452
+ #### `@WindowManager<T>(config)`
453
+
454
+ Defines a BrowserWindow manager.
455
+
456
+ **Parameters:**
457
+
458
+ - `hash: string` - Unique window identifier
459
+ - `isCache?: boolean` - Cache window instance
460
+ - `options: BrowserWindowConstructorOptions` - Electron window options
461
+
462
+ ```typescript
463
+ @WindowManager<TWindows["myWindow"]>({
464
+ hash: "window:my-window",
465
+ isCache: true,
466
+ options: { width: 800, height: 600 },
467
+ })
468
+ export class MyWindow implements TWindowManager {
469
+ onWebContentsDidFinishLoad(window: BrowserWindow): void {
470
+ // Lifecycle hook
471
+ }
472
+ }
473
+ ```
474
+
475
+ ### Core Functions
476
+
477
+ #### `initSettings(config)`
478
+
479
+ Initializes framework configuration.
480
+
481
+ **Parameters:**
482
+
483
+ - `baseRestApi: string` - Base REST API URL
484
+ - `localhostPort: string` - Development server port
485
+ - `folders: { distRenderer: string; distMain: string }` - Build output folders
486
+
487
+ ```typescript
488
+ initSettings({
489
+ baseRestApi: process.env.BASE_REST_API ?? "",
490
+ localhostPort: process.env.LOCALHOST_ELECTRON_SERVER_PORT ?? "",
491
+ folders: {
492
+ distRenderer: "dist-renderer",
493
+ distMain: "dist-main",
494
+ },
495
+ });
496
+ ```
497
+
498
+ #### `bootstrapModules(modules[])`
499
+
500
+ Bootstraps all modules and initializes the DI container.
501
+
502
+ ```typescript
503
+ await bootstrapModules([AppModule, AuthModule, ResourcesModule]);
504
+ ```
505
+
506
+ #### `getWindow<T>(hash)`
507
+
508
+ Retrieves a window instance by its hash identifier.
509
+
510
+ ```typescript
511
+ const mainWindow = getWindow<TWindows["main"]>("window:main");
512
+ const window = await mainWindow.create();
513
+ ```
514
+
515
+ #### `destroyWindows()`
516
+
517
+ Destroys all cached windows.
518
+
519
+ ```typescript
520
+ ...
521
+ import { destroyWindows } from "@devisfuture/electron-modular";
522
+ ...
523
+
524
+ app.on("before-quit", () => {
525
+ destroyWindows();
526
+ });
527
+ ```
528
+
529
+ ### Lifecycle Interfaces
530
+
531
+ #### `TIpcHandlerInterface`
532
+
533
+ Interface for IPC handlers.
534
+
535
+ ```typescript
536
+ export interface TIpcHandlerInterface {
537
+ onInit?(params: TParamOnInit): void | Promise<void>;
538
+ }
539
+ ```
540
+
541
+ #### `TWindowManager`
542
+
543
+ Interface for window managers.
544
+
545
+ ```typescript
546
+ export interface TWindowManager {
547
+ onWebContentsDidFinishLoad?(window: BrowserWindow): void;
548
+ }
549
+ ```
550
+
551
+ ---
552
+
553
+ ## Module Structure
554
+
555
+ Recommended file organization for a feature module:
556
+
557
+ ```
558
+ my-feature/
559
+ ├── module.ts # Module definition with @RgModule
560
+ ├── service.ts # Main business logic service
561
+ ├── ipc.ts # IPC communication handlers
562
+ ├── window.ts # BrowserWindow manager
563
+ ├── tokens.ts # Dependency injection tokens
564
+ ├── types.ts # TypeScript type definitions
565
+ └── services/ # Additional services (optional)
566
+ ├── helper.ts
567
+ └── validator.ts
568
+ ```
569
+
570
+ ---
571
+
572
+ ## Best Practices
573
+
574
+ ### 1. Use Providers for Cross-Module Communication
575
+
576
+ ✅ **Good:**
577
+
578
+ ```typescript
579
+ {
580
+ provide: AUTH_PROVIDER,
581
+ useFactory: (authService: AuthService): TAuthProvider => ({
582
+ checkAuthenticated: (window) => authService.checkAuthenticated(window),
583
+ logout: (window) => authService.logout(window),
584
+ }),
585
+ inject: [AuthService],
586
+ }
587
+ ```
588
+
589
+ ❌ **Bad:**
590
+
591
+ ```typescript
592
+ // Don't export entire service
593
+ exports: [AuthService];
594
+ ```
595
+
596
+ ### 2. Keep Services Focused
597
+
598
+ Each service should have a single responsibility.
599
+
600
+ ✅ **Good:**
601
+
602
+ ```typescript
603
+ @Injectable()
604
+ export class ResourcesService {
605
+ // Only handles resource data operations
606
+ }
607
+
608
+ @Injectable()
609
+ export class CacheWindowsService {
610
+ // Only handles window caching
611
+ }
612
+ ```
613
+
614
+ ### 3. Use Tokens for All Cross-Module Dependencies
615
+
616
+ ✅ **Good:**
617
+
618
+ ```typescript
619
+ export const RESOURCES_REST_API_PROVIDER = Symbol("RESOURCES_REST_API_PROVIDER");
620
+
621
+ constructor(
622
+ @Inject(RESOURCES_REST_API_PROVIDER) private restApiProvider
623
+ ) {}
624
+ ```
625
+
626
+ ### 4. Implement Lifecycle Hooks
627
+
628
+ Use lifecycle hooks for initialization logic.
629
+
630
+ ```typescript
631
+ @IpcHandler()
632
+ export class MyIpc implements TIpcHandlerInterface {
633
+ async onInit({ getWindow }: TParamOnInit) {
634
+ // Initialize IPC listeners
635
+ }
636
+ }
637
+
638
+ @WindowManager(config)
639
+ export class MyWindow implements TWindowManager {
640
+ onWebContentsDidFinishLoad(window: BrowserWindow): void {
641
+ // Initialize when content loads
642
+ }
643
+ }
644
+ ```
645
+
646
+ ### 5. Type Everything
647
+
648
+ Use TypeScript for all services, providers, and interfaces.
649
+ Decorators Reference
650
+
651
+ ### `@RgModule(config)`
652
+
653
+ Defines a module.
654
+
655
+ - `imports?: Class[]` - Modules to import
656
+ - `providers?: Provider[]` - Services and factories
657
+ - `ipc?: Class[]` - IPC handler classes
658
+ - `windows?: Class[]` - Window manager classes
659
+ - `exports?: Class[]` - Providers to export
660
+
661
+ ### `@Injectable()`
662
+
663
+ Makes a class injectable.
664
+
665
+ ```typescript
666
+ @Injectable()
667
+ export class MyService {}
668
+ ```
669
+
670
+ ### `@Inject(token)`
671
+
672
+ Injects a dependency by token.
673
+
674
+ ```typescript
675
+ constructor(@Inject(MY_PROVIDER) private provider: TMyProvider) {}
676
+ ```
677
+
678
+ ### `@IpcHandler()`
679
+
680
+ Marks a class as IPC handler.
681
+
682
+ ```typescript
683
+ @IpcHandler()
684
+ export class MyIpc implements TIpcHandlerInterface {}
685
+ ```
686
+
687
+ ### `@WindowManager<T>(config)`
688
+
689
+ Defines a window manager.
690
+
691
+ ```typescript
692
+ @WindowManager<TWindows["myWindow"]>({
693
+ hash: "window:my-window",
694
+ isCache: true,
695
+ options: { width: 800, height: 600 },
696
+ })
697
+ export class MyWindow implements TWindowManager {}
698
+ ```
699
+
700
+ ---
701
+
702
+ ## Key Functions
703
+
704
+ ### `initSettings(config)`
705
+
706
+ ```typescript
707
+ initSettings({
708
+ baseRestApi: process.env.BASE_REST_API ?? "",
709
+ localhostPort: process.env.LOCALHOST_ELECTRON_SERVER_PORT ?? "",
710
+ folders: { distRenderer: "dist-renderer", distMain: "dist-main" },
711
+ });
712
+ ```
713
+
714
+ ### `bootstrapModules(modules[])`
715
+
716
+ ```typescript
717
+ await bootstrapModules([AppModule, UserModule]);
718
+ ```
719
+
720
+ ### `getWindow<T>(hash)`
721
+
722
+ ```typescript
723
+ const mainWindow = getWindow<TWindows["main"]>("window:main");
724
+ const window = await mainWindow.create();
725
+ ```
726
+
727
+ ### `destroyWindows()`
728
+
729
+ ```typescript
730
+ app.on("before-quit", () => destroyWindows());
731
+ ```