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