@devisfuture/electron-modular 1.0.11 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -81,7 +81,9 @@ import { UserModule } from "./user/module.js";
|
|
|
81
81
|
import { ResourcesModule } from "./resources/module.js";
|
|
82
82
|
|
|
83
83
|
initSettings({
|
|
84
|
-
|
|
84
|
+
cspConnectSources: process.env.BASE_REST_API
|
|
85
|
+
? [process.env.BASE_REST_API]
|
|
86
|
+
: [],
|
|
85
87
|
localhostPort: process.env.LOCALHOST_ELECTRON_SERVER_PORT ?? "",
|
|
86
88
|
folders: {
|
|
87
89
|
distRenderer: "dist-renderer",
|
|
@@ -266,6 +268,7 @@ export class UserService {
|
|
|
266
268
|
Handle communication between main and renderer processes.
|
|
267
269
|
|
|
268
270
|
```typescript
|
|
271
|
+
import { ipcMain, type IpcMainEvent } from "electron";
|
|
269
272
|
import {
|
|
270
273
|
IpcHandler,
|
|
271
274
|
TIpcHandlerInterface,
|
|
@@ -280,7 +283,7 @@ export class UserIpc implements TIpcHandlerInterface {
|
|
|
280
283
|
async onInit({ getWindow }: TParamOnInit<TWindows["main"]>) {
|
|
281
284
|
const mainWindow = getWindow("window:main");
|
|
282
285
|
|
|
283
|
-
|
|
286
|
+
ipcMain.on("user:fetch", (event: IpcMainEvent, userId: string) => {
|
|
284
287
|
const user = await this.userService.byId(userId);
|
|
285
288
|
event.reply("user:fetch:response", user);
|
|
286
289
|
});
|
|
@@ -370,6 +373,80 @@ Important implementation notes ⚠️
|
|
|
370
373
|
- Handlers are attached per BrowserWindow instance and cleaned up automatically when the window is closed, so you don't have to manually remove listeners.
|
|
371
374
|
- The same instance and set of handlers are tracked in a WeakMap internally; re-attaching the same `windowInstance` will not duplicate listeners.
|
|
372
375
|
|
|
376
|
+
### Opening windows with URL params (dynamic routes) 🔗
|
|
377
|
+
|
|
378
|
+
If your renderer uses dynamic routes (for example React Router) you can open a window that targets a specific route by passing a `hash` to `create`. The `hash` is merged with the window manager's defaults and can carry route segments or parameters (for example `window:items/<id>`).
|
|
379
|
+
|
|
380
|
+
Renderer process
|
|
381
|
+
|
|
382
|
+
```tsx
|
|
383
|
+
<Route path="/window:items/:id" element={<ItemWindow />} />
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Renderer (inside `ItemWindow`)
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
import { useParams } from "react-router-dom";
|
|
390
|
+
|
|
391
|
+
const ItemWindow = () => {
|
|
392
|
+
const { id } = useParams<{ id?: string }>();
|
|
393
|
+
|
|
394
|
+
if (!id) return null;
|
|
395
|
+
|
|
396
|
+
return <span>item id: {id}</span>;
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
export default ItemWindow;
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Main process
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
import { ipcMain, type IpcMainEvent } from "electron";
|
|
406
|
+
import { IpcHandler, type TParamOnInit } from "@devisfuture/electron-modular";
|
|
407
|
+
|
|
408
|
+
@IpcHandler()
|
|
409
|
+
export class ItemsIpc {
|
|
410
|
+
constructor() {}
|
|
411
|
+
|
|
412
|
+
onInit({ getWindow }: TParamOnInit<TWindows["items"]>): void {
|
|
413
|
+
const window = getWindow("window:items");
|
|
414
|
+
|
|
415
|
+
ipcMain.on(
|
|
416
|
+
"itemWindow",
|
|
417
|
+
async (_: IpcMainEvent, payload: { id?: string }) => {
|
|
418
|
+
if (payload.id === undefined) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
await window.create({
|
|
423
|
+
hash: `window:items/${payload.id}`,
|
|
424
|
+
});
|
|
425
|
+
},
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Default manager example
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
@WindowManager<TWindows['items']>({
|
|
435
|
+
hash: 'window:items',
|
|
436
|
+
isCache: true,
|
|
437
|
+
options: {
|
|
438
|
+
width: 350,
|
|
439
|
+
height: 300,
|
|
440
|
+
},
|
|
441
|
+
})
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Notes:
|
|
445
|
+
|
|
446
|
+
- `await window.create({...})` merges the provided options with the manager's default `options`.
|
|
447
|
+
- When `isCache: true`, `getWindow('window:items')` returns the cached manager and `create` will reuse (or re-create) the BrowserWindow as implemented by the manager.
|
|
448
|
+
- Use a unique `hash` per route instance (for example `window:items/<id>`), so the renderer can read the route from the window URL and navigate to the correct route when the window loads.
|
|
449
|
+
|
|
373
450
|
---
|
|
374
451
|
|
|
375
452
|
## TypeScript types — `TWindows["myWindow"]`
|
|
@@ -482,13 +559,17 @@ Initializes framework configuration.
|
|
|
482
559
|
|
|
483
560
|
**Parameters:**
|
|
484
561
|
|
|
485
|
-
- `
|
|
562
|
+
- `cspConnectSources?: string[]` - Optional array of origins to include in the `connect-src` directive of the generated Content-Security-Policy header.
|
|
486
563
|
- `localhostPort: string` - Development server port
|
|
487
564
|
- `folders: { distRenderer: string; distMain: string }` - Build output folders
|
|
488
565
|
|
|
489
566
|
```typescript
|
|
490
567
|
initSettings({
|
|
491
|
-
|
|
568
|
+
cspConnectSources: [
|
|
569
|
+
"https://api.example.com",
|
|
570
|
+
"https://cdn.example.com",
|
|
571
|
+
"wss://websocket.example.com",
|
|
572
|
+
],
|
|
492
573
|
localhostPort: process.env.LOCALHOST_ELECTRON_SERVER_PORT ?? "",
|
|
493
574
|
folders: {
|
|
494
575
|
distRenderer: "dist-renderer",
|
|
@@ -497,6 +578,12 @@ initSettings({
|
|
|
497
578
|
});
|
|
498
579
|
```
|
|
499
580
|
|
|
581
|
+
> Note: When a cached window is created the framework will set a Content-Security-Policy header for renderer responses. The `connect-src` directive will include `'self'` plus any entries from `cspConnectSources`. For example:
|
|
582
|
+
|
|
583
|
+
```
|
|
584
|
+
connect-src 'self' https://api.example.com https://cdn.example.com wss://websocket.example.com;
|
|
585
|
+
```
|
|
586
|
+
|
|
500
587
|
#### `bootstrapModules(modules[])`
|
|
501
588
|
|
|
502
589
|
Bootstraps all modules and initializes the DI container.
|
|
@@ -707,7 +794,9 @@ export class MyWindow implements TWindowManager {}
|
|
|
707
794
|
|
|
708
795
|
```typescript
|
|
709
796
|
initSettings({
|
|
710
|
-
|
|
797
|
+
cspConnectSources: process.env.BASE_REST_API
|
|
798
|
+
? [process.env.BASE_REST_API]
|
|
799
|
+
: [],
|
|
711
800
|
localhostPort: process.env.LOCALHOST_ELECTRON_SERVER_PORT ?? "",
|
|
712
801
|
folders: { distRenderer: "dist-renderer", distMain: "dist-main" },
|
|
713
802
|
});
|
|
@@ -3,8 +3,9 @@ import path from "node:path";
|
|
|
3
3
|
import { cacheWindows } from "./cache.js";
|
|
4
4
|
import { getWindow } from "./receive.js";
|
|
5
5
|
import { getSettings } from "../bootstrap/settings.js";
|
|
6
|
-
const setupCSP = (
|
|
7
|
-
const
|
|
6
|
+
const setupCSP = (sources, dev) => {
|
|
7
|
+
const connectSrc = sources.length > 0 ? ` ${sources.join(" ")}` : "";
|
|
8
|
+
const csp = `default-src 'self'; connect-src 'self'${connectSrc}; img-src * data:; style-src 'self' 'unsafe-inline'; script-src 'self' ${dev ? "'unsafe-inline'" : ""};`
|
|
8
9
|
.replace(/\s{2,}/g, " ")
|
|
9
10
|
.trim();
|
|
10
11
|
session.defaultSession.webRequest.onHeadersReceived((d, cb) => {
|
|
@@ -21,8 +22,6 @@ export const createWindow = ({ hash, options, isCache, loadURL, }) => {
|
|
|
21
22
|
const isDev = process.env.NODE_ENV === "development";
|
|
22
23
|
const ui = path.join(app.getAppPath(), `/${settings.folders.distRenderer}/index.html`);
|
|
23
24
|
const preload = path.join(app.getAppPath(), isDev ? "." : "..", `/${settings.folders.distMain}/preload.cjs`);
|
|
24
|
-
if (!settings.baseRestApi)
|
|
25
|
-
console.warn('Warning: You have to add an environment variable called "process.env.BASE_REST_API"!');
|
|
26
25
|
if (!settings.localhostPort)
|
|
27
26
|
console.warn('Warning: You have to add an environment variable called "process.env.LOCALHOST_ELECTRON_SERVER_PORT"!');
|
|
28
27
|
if (hash && isCache) {
|
|
@@ -41,8 +40,8 @@ export const createWindow = ({ hash, options, isCache, loadURL, }) => {
|
|
|
41
40
|
...options?.webPreferences,
|
|
42
41
|
},
|
|
43
42
|
});
|
|
44
|
-
if (isCache && !loadURL)
|
|
45
|
-
setupCSP(settings.
|
|
43
|
+
if (isCache && !loadURL && settings.cspConnectSources)
|
|
44
|
+
setupCSP(settings.cspConnectSources, isDev);
|
|
46
45
|
if (loadURL) {
|
|
47
46
|
win.loadURL(loadURL);
|
|
48
47
|
}
|
package/package.json
CHANGED