@noxfly/noxus 1.2.0 → 2.0.0
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/.github/copilot-instructions.md +32 -0
- package/README.md +36 -127
- package/dist/{index-5OkVPHfI.d.mts → index-CI3OMzNR.d.mts} +52 -2
- package/dist/{index-5OkVPHfI.d.ts → index-CI3OMzNR.d.ts} +52 -2
- package/dist/main.d.mts +34 -14
- package/dist/main.d.ts +34 -14
- package/dist/main.js +342 -31
- package/dist/main.mjs +339 -31
- package/dist/renderer.d.mts +1 -1
- package/dist/renderer.d.ts +1 -1
- package/dist/renderer.js +251 -1
- package/dist/renderer.mjs +250 -1
- package/package.json +1 -1
- package/src/DI/injector-explorer.ts +1 -1
- package/src/app.ts +24 -15
- package/src/decorators/injectable.decorator.ts +6 -17
- package/src/decorators/injectable.metadata.ts +15 -0
- package/src/index.ts +1 -0
- package/src/main.ts +3 -0
- package/src/preload-bridge.ts +75 -0
- package/src/renderer-client.ts +338 -0
- package/src/request.ts +1 -0
- package/src/router.ts +1 -1
- package/src/socket.ts +14 -9
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Lifetime } from "src/DI/app-injector";
|
|
2
|
+
|
|
3
|
+
export const INJECTABLE_METADATA_KEY = Symbol("INJECTABLE_METADATA_KEY");
|
|
4
|
+
|
|
5
|
+
export function defineInjectableMetadata(target: Function, lifetime: Lifetime): void {
|
|
6
|
+
Reflect.defineMetadata(INJECTABLE_METADATA_KEY, lifetime, target);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getInjectableMetadata(target: Function): Lifetime | undefined {
|
|
10
|
+
return Reflect.getMetadata(INJECTABLE_METADATA_KEY, target) as Lifetime | undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function hasInjectableMetadata(target: Function): boolean {
|
|
14
|
+
return Reflect.hasMetadata(INJECTABLE_METADATA_KEY, target);
|
|
15
|
+
}
|
package/src/index.ts
CHANGED
package/src/main.ts
CHANGED
|
@@ -18,8 +18,11 @@ export * from './decorators/controller.decorator';
|
|
|
18
18
|
export * from './decorators/injectable.decorator';
|
|
19
19
|
export * from './decorators/method.decorator';
|
|
20
20
|
export * from './decorators/module.decorator';
|
|
21
|
+
export * from './preload-bridge';
|
|
21
22
|
export * from './utils/logger';
|
|
22
23
|
export * from './utils/types';
|
|
23
24
|
export * from './request';
|
|
24
25
|
export * from './renderer-events';
|
|
26
|
+
export * from './renderer-client';
|
|
25
27
|
export * from './socket';
|
|
28
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright 2025 NoxFly
|
|
3
|
+
* @license MIT
|
|
4
|
+
* @author NoxFly
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { contextBridge, ipcRenderer } from 'electron/renderer';
|
|
8
|
+
import type { IPortRequester } from 'src/renderer-client';
|
|
9
|
+
|
|
10
|
+
export interface NoxusPreloadAPI extends IPortRequester {}
|
|
11
|
+
|
|
12
|
+
export interface NoxusPreloadOptions {
|
|
13
|
+
exposeAs?: string;
|
|
14
|
+
initMessageType?: string;
|
|
15
|
+
requestChannel?: string;
|
|
16
|
+
responseChannel?: string;
|
|
17
|
+
targetWindow?: Window;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const DEFAULT_EXPOSE_NAME = 'noxus';
|
|
21
|
+
const DEFAULT_INIT_EVENT = 'init-port';
|
|
22
|
+
const DEFAULT_REQUEST_CHANNEL = 'gimme-my-port';
|
|
23
|
+
const DEFAULT_RESPONSE_CHANNEL = 'port';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Exposes a minimal bridge in the isolated preload context so renderer processes
|
|
27
|
+
* can request the two MessagePorts required by Noxus. The bridge forwards both
|
|
28
|
+
* request/response and socket ports to the renderer via window.postMessage.
|
|
29
|
+
*/
|
|
30
|
+
export function exposeNoxusBridge(options: NoxusPreloadOptions = {}): NoxusPreloadAPI {
|
|
31
|
+
const {
|
|
32
|
+
exposeAs = DEFAULT_EXPOSE_NAME,
|
|
33
|
+
initMessageType = DEFAULT_INIT_EVENT,
|
|
34
|
+
requestChannel = DEFAULT_REQUEST_CHANNEL,
|
|
35
|
+
responseChannel = DEFAULT_RESPONSE_CHANNEL,
|
|
36
|
+
targetWindow = window,
|
|
37
|
+
} = options;
|
|
38
|
+
|
|
39
|
+
const api: NoxusPreloadAPI = {
|
|
40
|
+
requestPort: () => {
|
|
41
|
+
ipcRenderer.send(requestChannel);
|
|
42
|
+
|
|
43
|
+
ipcRenderer.once(responseChannel, (event, message: { senderId: number }) => {
|
|
44
|
+
const ports = (event.ports ?? []).filter((port): port is MessagePort => port !== undefined);
|
|
45
|
+
|
|
46
|
+
if(ports.length === 0) {
|
|
47
|
+
console.error('[Noxus] No MessagePort received from main process.');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for(const port of ports) {
|
|
52
|
+
try {
|
|
53
|
+
port.start();
|
|
54
|
+
}
|
|
55
|
+
catch(error) {
|
|
56
|
+
console.error('[Noxus] Failed to start MessagePort.', error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
targetWindow.postMessage(
|
|
61
|
+
{
|
|
62
|
+
type: initMessageType,
|
|
63
|
+
senderId: message?.senderId,
|
|
64
|
+
},
|
|
65
|
+
'*',
|
|
66
|
+
ports,
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
contextBridge.exposeInMainWorld(exposeAs, api);
|
|
73
|
+
|
|
74
|
+
return api;
|
|
75
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright 2025 NoxFly
|
|
3
|
+
* @license MIT
|
|
4
|
+
* @author NoxFly
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { IBatchRequestItem, IBatchResponsePayload, IRequest, IResponse } from 'src/request';
|
|
8
|
+
import { RendererEventRegistry } from 'src/renderer-events';
|
|
9
|
+
|
|
10
|
+
export interface IPortRequester {
|
|
11
|
+
requestPort(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface RendererClientOptions {
|
|
15
|
+
bridge?: IPortRequester | null;
|
|
16
|
+
bridgeName?: string | string[];
|
|
17
|
+
initMessageType?: string;
|
|
18
|
+
windowRef?: Window;
|
|
19
|
+
generateRequestId?: () => string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface PendingRequest<T = unknown> {
|
|
23
|
+
resolve: (value: T) => void;
|
|
24
|
+
reject: (reason: IResponse<T>) => void;
|
|
25
|
+
request: IRequest;
|
|
26
|
+
submittedAt: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const DEFAULT_INIT_EVENT = 'init-port';
|
|
30
|
+
const DEFAULT_BRIDGE_NAMES = ['noxus', 'ipcRenderer'];
|
|
31
|
+
|
|
32
|
+
function defaultRequestId(): string {
|
|
33
|
+
if(typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
34
|
+
return crypto.randomUUID();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return `${Date.now().toString(16)}-${Math.floor(Math.random() * 1e8).toString(16)}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeBridgeNames(preferred?: string | string[]): string[] {
|
|
41
|
+
const names: string[] = [];
|
|
42
|
+
|
|
43
|
+
const add = (name: string | undefined): void => {
|
|
44
|
+
if(!name)
|
|
45
|
+
return;
|
|
46
|
+
|
|
47
|
+
if(!names.includes(name)) {
|
|
48
|
+
names.push(name);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
if(Array.isArray(preferred)) {
|
|
53
|
+
for(const name of preferred) {
|
|
54
|
+
add(name);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
add(preferred);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for(const fallback of DEFAULT_BRIDGE_NAMES) {
|
|
62
|
+
add(fallback);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return names;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resolveBridgeFromWindow(windowRef: Window, preferred?: string | string[]): IPortRequester | null {
|
|
69
|
+
const names = normalizeBridgeNames(preferred);
|
|
70
|
+
const globalRef = windowRef as unknown as Record<string, unknown> | null | undefined;
|
|
71
|
+
|
|
72
|
+
if(!globalRef) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for(const name of names) {
|
|
77
|
+
const candidate = globalRef[name];
|
|
78
|
+
|
|
79
|
+
if(candidate && typeof (candidate as IPortRequester).requestPort === 'function') {
|
|
80
|
+
return candidate as IPortRequester;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export class NoxRendererClient {
|
|
88
|
+
public readonly events = new RendererEventRegistry();
|
|
89
|
+
|
|
90
|
+
protected readonly pendingRequests = new Map<string, PendingRequest>();
|
|
91
|
+
|
|
92
|
+
protected requestPort: MessagePort | undefined;
|
|
93
|
+
protected socketPort: MessagePort | undefined;
|
|
94
|
+
protected senderId: number | undefined;
|
|
95
|
+
|
|
96
|
+
private readonly bridge: IPortRequester | null;
|
|
97
|
+
private readonly initMessageType: string;
|
|
98
|
+
private readonly windowRef: Window;
|
|
99
|
+
private readonly generateRequestId: () => string;
|
|
100
|
+
|
|
101
|
+
private isReady = false;
|
|
102
|
+
private setupPromise: Promise<void> | undefined;
|
|
103
|
+
private setupResolve: (() => void) | undefined;
|
|
104
|
+
private setupReject: ((reason: Error) => void) | undefined;
|
|
105
|
+
|
|
106
|
+
constructor(options: RendererClientOptions = {}) {
|
|
107
|
+
this.windowRef = options.windowRef ?? window;
|
|
108
|
+
const resolvedBridge = options.bridge ?? resolveBridgeFromWindow(this.windowRef, options.bridgeName);
|
|
109
|
+
this.bridge = resolvedBridge ?? null;
|
|
110
|
+
this.initMessageType = options.initMessageType ?? DEFAULT_INIT_EVENT;
|
|
111
|
+
this.generateRequestId = options.generateRequestId ?? defaultRequestId;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public async setup(): Promise<void> {
|
|
115
|
+
if(this.isReady) {
|
|
116
|
+
return Promise.resolve();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if(this.setupPromise) {
|
|
120
|
+
return this.setupPromise;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if(!this.bridge || typeof this.bridge.requestPort !== 'function') {
|
|
124
|
+
throw new Error('[Noxus] Renderer bridge is missing requestPort().');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.setupPromise = new Promise<void>((resolve, reject) => {
|
|
128
|
+
this.setupResolve = resolve;
|
|
129
|
+
this.setupReject = reject;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
this.windowRef.addEventListener('message', this.onWindowMessage);
|
|
133
|
+
this.bridge.requestPort();
|
|
134
|
+
|
|
135
|
+
return this.setupPromise;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public dispose(): void {
|
|
139
|
+
this.windowRef.removeEventListener('message', this.onWindowMessage);
|
|
140
|
+
|
|
141
|
+
this.requestPort?.close();
|
|
142
|
+
this.socketPort?.close();
|
|
143
|
+
|
|
144
|
+
this.requestPort = undefined;
|
|
145
|
+
this.socketPort = undefined;
|
|
146
|
+
this.senderId = undefined;
|
|
147
|
+
this.isReady = false;
|
|
148
|
+
|
|
149
|
+
this.pendingRequests.clear();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public async request<TResponse, TBody = unknown>(request: Omit<IRequest<TBody>, 'requestId' | 'senderId'>): Promise<TResponse> {
|
|
153
|
+
const senderId = this.senderId;
|
|
154
|
+
const requestId = this.generateRequestId();
|
|
155
|
+
|
|
156
|
+
if(senderId === undefined) {
|
|
157
|
+
return Promise.reject(this.createErrorResponse(requestId, 'MessagePort is not available'));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const readinessError = this.validateReady(requestId);
|
|
161
|
+
|
|
162
|
+
if(readinessError) {
|
|
163
|
+
return Promise.reject(readinessError as IResponse<TResponse>);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const message: IRequest<TBody> = {
|
|
167
|
+
requestId,
|
|
168
|
+
senderId,
|
|
169
|
+
...request,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
return new Promise<TResponse>((resolve, reject) => {
|
|
173
|
+
const pending: PendingRequest<TResponse> = {
|
|
174
|
+
resolve,
|
|
175
|
+
reject: (response: IResponse<TResponse>) => reject(response),
|
|
176
|
+
request: message,
|
|
177
|
+
submittedAt: Date.now(),
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
this.pendingRequests.set(message.requestId, pending as PendingRequest);
|
|
181
|
+
|
|
182
|
+
this.requestPort!.postMessage(message);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
public async batch(requests: Omit<IBatchRequestItem<unknown>, 'requestId'>[]): Promise<IBatchResponsePayload> {
|
|
187
|
+
return this.request<IBatchResponsePayload>({
|
|
188
|
+
method: 'BATCH',
|
|
189
|
+
path: '',
|
|
190
|
+
body: {
|
|
191
|
+
requests,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public getSenderId(): number | undefined {
|
|
197
|
+
return this.senderId;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private readonly onWindowMessage = (event: MessageEvent): void => {
|
|
201
|
+
if(event.data?.type !== this.initMessageType) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if(!Array.isArray(event.ports) || event.ports.length < 2) {
|
|
206
|
+
const error = new Error('[Noxus] Renderer expected two MessagePorts (request + socket).');
|
|
207
|
+
|
|
208
|
+
console.error(error);
|
|
209
|
+
this.setupReject?.(error);
|
|
210
|
+
this.resetSetupState();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this.windowRef.removeEventListener('message', this.onWindowMessage);
|
|
215
|
+
|
|
216
|
+
this.requestPort = event.ports[0];
|
|
217
|
+
this.socketPort = event.ports[1];
|
|
218
|
+
this.senderId = event.data.senderId;
|
|
219
|
+
|
|
220
|
+
if(this.requestPort === undefined || this.socketPort === undefined) {
|
|
221
|
+
const error = new Error('[Noxus] Renderer failed to receive valid MessagePorts.');
|
|
222
|
+
console.error(error);
|
|
223
|
+
this.setupReject?.(error);
|
|
224
|
+
this.resetSetupState();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
this.attachRequestPort(this.requestPort);
|
|
229
|
+
this.attachSocketPort(this.socketPort);
|
|
230
|
+
|
|
231
|
+
this.isReady = true;
|
|
232
|
+
this.setupResolve?.();
|
|
233
|
+
this.resetSetupState(true);
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
private readonly onSocketMessage = (event: MessageEvent): void => {
|
|
237
|
+
if(this.events.tryDispatchFromMessageEvent(event)) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
console.warn('[Noxus] Received a socket message that is not a renderer event payload.', event.data);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
private readonly onRequestMessage = (event: MessageEvent): void => {
|
|
245
|
+
if(this.events.tryDispatchFromMessageEvent(event)) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const response: IResponse = event.data;
|
|
250
|
+
|
|
251
|
+
if(!response || typeof response.requestId !== 'string') {
|
|
252
|
+
console.error('[Noxus] Renderer received an invalid response payload.', response);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const pending = this.pendingRequests.get(response.requestId);
|
|
257
|
+
|
|
258
|
+
if(!pending) {
|
|
259
|
+
console.error(`[Noxus] No pending handler found for request ${response.requestId}.`);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this.pendingRequests.delete(response.requestId);
|
|
264
|
+
|
|
265
|
+
this.onRequestCompleted(pending, response);
|
|
266
|
+
|
|
267
|
+
if(response.status >= 400) {
|
|
268
|
+
pending.reject(response as IResponse<any>);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
pending.resolve(response.body as unknown);
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
protected onRequestCompleted(pending: PendingRequest, response: IResponse): void {
|
|
276
|
+
if(typeof console.groupCollapsed === 'function') {
|
|
277
|
+
console.groupCollapsed(`${response.status} ${pending.request.method} /${pending.request.path}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if(response.error) {
|
|
281
|
+
console.error('error message:', response.error);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if(response.body !== undefined) {
|
|
285
|
+
console.info('response:', response.body);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
console.info('request:', pending.request);
|
|
289
|
+
console.info(`Request duration: ${Date.now() - pending.submittedAt} ms`);
|
|
290
|
+
|
|
291
|
+
if(typeof console.groupCollapsed === 'function') {
|
|
292
|
+
console.groupEnd();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private attachRequestPort(port: MessagePort): void {
|
|
297
|
+
port.onmessage = this.onRequestMessage;
|
|
298
|
+
port.start();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private attachSocketPort(port: MessagePort): void {
|
|
302
|
+
port.onmessage = this.onSocketMessage;
|
|
303
|
+
port.start();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private validateReady(requestId: string): IResponse | undefined {
|
|
307
|
+
if(!this.isElectronEnvironment()) {
|
|
308
|
+
return this.createErrorResponse(requestId, 'Not running in Electron environment');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if(!this.requestPort) {
|
|
312
|
+
return this.createErrorResponse(requestId, 'MessagePort is not available');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
private createErrorResponse<T>(requestId: string, message: string): IResponse<T> {
|
|
319
|
+
return {
|
|
320
|
+
status: 500,
|
|
321
|
+
requestId,
|
|
322
|
+
error: message,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private resetSetupState(success = false): void {
|
|
327
|
+
if(!success) {
|
|
328
|
+
this.setupPromise = undefined;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
this.setupResolve = undefined;
|
|
332
|
+
this.setupReject = undefined;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
public isElectronEnvironment(): boolean {
|
|
336
|
+
return typeof window !== 'undefined' && /Electron/.test(window.navigator.userAgent);
|
|
337
|
+
}
|
|
338
|
+
}
|
package/src/request.ts
CHANGED
package/src/router.ts
CHANGED
|
@@ -213,7 +213,7 @@ export class Router {
|
|
|
213
213
|
|
|
214
214
|
for(const [index, item] of payload.requests.entries()) {
|
|
215
215
|
const subRequestId = item.requestId ?? `${request.id}:${index}`;
|
|
216
|
-
const atomicRequest = new Request(request.event, subRequestId, item.method, item.path, item.body);
|
|
216
|
+
const atomicRequest = new Request(request.event, request.senderId, subRequestId, item.method, item.path, item.body);
|
|
217
217
|
batchResponses.push(await this.handleAtomic(atomicRequest));
|
|
218
218
|
}
|
|
219
219
|
|
package/src/socket.ts
CHANGED
|
@@ -12,24 +12,29 @@ import { Injectable } from 'src/decorators/injectable.decorator';
|
|
|
12
12
|
import { createRendererEventMessage } from 'src/request';
|
|
13
13
|
import { Logger } from 'src/utils/logger';
|
|
14
14
|
|
|
15
|
+
interface RendererChannels {
|
|
16
|
+
request: Electron.MessageChannelMain;
|
|
17
|
+
socket: Electron.MessageChannelMain;
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
@Injectable('singleton')
|
|
16
21
|
export class NoxSocket {
|
|
17
|
-
private readonly
|
|
22
|
+
private readonly channels = new Map<number, RendererChannels>();
|
|
18
23
|
|
|
19
|
-
public register(senderId: number,
|
|
20
|
-
this.
|
|
24
|
+
public register(senderId: number, requestChannel: Electron.MessageChannelMain, socketChannel: Electron.MessageChannelMain): void {
|
|
25
|
+
this.channels.set(senderId, { request: requestChannel, socket: socketChannel });
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
public get(senderId: number):
|
|
24
|
-
return this.
|
|
28
|
+
public get(senderId: number): RendererChannels | undefined {
|
|
29
|
+
return this.channels.get(senderId);
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
public unregister(senderId: number): void {
|
|
28
|
-
this.
|
|
33
|
+
this.channels.delete(senderId);
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
public getSenderIds(): number[] {
|
|
32
|
-
return [...this.
|
|
37
|
+
return [...this.channels.keys()];
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
public emit<TPayload = unknown>(eventName: string, payload?: TPayload, targetSenderIds?: number[]): number {
|
|
@@ -43,7 +48,7 @@ export class NoxSocket {
|
|
|
43
48
|
let delivered = 0;
|
|
44
49
|
|
|
45
50
|
for(const senderId of recipients) {
|
|
46
|
-
const channel = this.
|
|
51
|
+
const channel = this.channels.get(senderId);
|
|
47
52
|
|
|
48
53
|
if(!channel) {
|
|
49
54
|
Logger.warn(`No message channel found for sender ID: ${senderId} while emitting "${normalizedEvent}".`);
|
|
@@ -51,7 +56,7 @@ export class NoxSocket {
|
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
try {
|
|
54
|
-
channel.port1.postMessage(createRendererEventMessage(normalizedEvent, payload));
|
|
59
|
+
channel.socket.port1.postMessage(createRendererEventMessage(normalizedEvent, payload));
|
|
55
60
|
delivered++;
|
|
56
61
|
}
|
|
57
62
|
catch(error) {
|