@3fv/electron-utility-process-manager 1.0.8 → 1.1.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.
Files changed (50) hide show
  1. package/README.md +41 -3
  2. package/lib/cjs/common/clients.d.ts +23 -1
  3. package/lib/cjs/common/clients.js +21 -2
  4. package/lib/cjs/common/clients.js.map +1 -1
  5. package/lib/cjs/common/events.d.ts +23 -0
  6. package/lib/cjs/common/events.js +48 -0
  7. package/lib/cjs/common/events.js.map +1 -0
  8. package/lib/cjs/common/index.d.ts +1 -0
  9. package/lib/cjs/common/index.js +1 -0
  10. package/lib/cjs/common/index.js.map +1 -1
  11. package/lib/cjs/common/types.d.ts +5 -0
  12. package/lib/cjs/main/UPMMainService.d.ts +19 -4
  13. package/lib/cjs/main/UPMMainService.js +33 -8
  14. package/lib/cjs/main/UPMMainService.js.map +1 -1
  15. package/lib/cjs/main/UPMMainServiceManager.js +1 -1
  16. package/lib/cjs/main/UPMMainServiceManager.js.map +1 -1
  17. package/lib/cjs/node/UPMNodeProcess.d.ts +23 -0
  18. package/lib/cjs/node/UPMNodeProcess.js +64 -9
  19. package/lib/cjs/node/UPMNodeProcess.js.map +1 -1
  20. package/lib/cjs/renderer/UPMRendererClientFactory.js +1 -1
  21. package/lib/cjs/renderer/UPMRendererClientFactory.js.map +1 -1
  22. package/lib/mjs/common/clients.d.ts +23 -1
  23. package/lib/mjs/common/clients.js +21 -2
  24. package/lib/mjs/common/clients.js.map +1 -1
  25. package/lib/mjs/common/events.d.ts +23 -0
  26. package/lib/mjs/common/events.js +41 -0
  27. package/lib/mjs/common/events.js.map +1 -0
  28. package/lib/mjs/common/index.d.ts +1 -0
  29. package/lib/mjs/common/index.js +1 -0
  30. package/lib/mjs/common/index.js.map +1 -1
  31. package/lib/mjs/common/types.d.ts +5 -0
  32. package/lib/mjs/main/UPMMainService.d.ts +19 -4
  33. package/lib/mjs/main/UPMMainService.js +34 -9
  34. package/lib/mjs/main/UPMMainService.js.map +1 -1
  35. package/lib/mjs/main/UPMMainServiceManager.js +1 -1
  36. package/lib/mjs/main/UPMMainServiceManager.js.map +1 -1
  37. package/lib/mjs/node/UPMNodeProcess.d.ts +23 -0
  38. package/lib/mjs/node/UPMNodeProcess.js +63 -8
  39. package/lib/mjs/node/UPMNodeProcess.js.map +1 -1
  40. package/lib/mjs/renderer/UPMRendererClientFactory.js +1 -1
  41. package/lib/mjs/renderer/UPMRendererClientFactory.js.map +1 -1
  42. package/package.json +5 -4
  43. package/src/common/clients.ts +42 -2
  44. package/src/common/events.ts +43 -0
  45. package/src/common/index.ts +1 -0
  46. package/src/common/types.ts +6 -0
  47. package/src/main/UPMMainService.ts +41 -9
  48. package/src/main/UPMMainServiceManager.ts +1 -1
  49. package/src/node/UPMNodeProcess.ts +73 -8
  50. package/src/renderer/UPMRendererClientFactory.ts +1 -1
@@ -1,15 +1,38 @@
1
1
  import * as UPM from "../common/index.js";
2
+ /**
3
+ * `clientId` of the main process, which is
4
+ * reached through `process.parentPort`
5
+ */
6
+ export declare const MainClientId = "main";
2
7
  export declare class UPMNodeProcess {
3
8
  readonly processPort: Electron.ParentPort;
4
9
  private readonly clientPorts_;
5
10
  private readonly requestHandlers_;
6
11
  private readonly eventHandlers_;
12
+ private messageIdSeq_;
7
13
  private onServiceClientPort;
8
14
  private onServiceMessage;
9
15
  private onMessage;
10
16
  private constructor();
11
17
  addRequestHandler<ReqMap extends UPM.MessageRequestMap = any, Type extends UPM.MessageRequestNames<ReqMap> = UPM.MessageRequestNames<ReqMap>>(type: Type, handler: UPM.RequestHandler<ReqMap, Type>): void;
12
18
  addEventHandler(handler: UPM.EventHandler): void;
19
+ /**
20
+ * The `clientId` of every client which can currently
21
+ * be pushed an event: the main process (`MainClientId`)
22
+ * and each client with an open port
23
+ */
24
+ get clientIds(): string[];
25
+ /**
26
+ * Push an event from the `utilityProcess` to its clients
27
+ * as `fire-and-forget`; a client receives it through
28
+ * the handlers it registered with `addEventHandler`.
29
+ *
30
+ * @param data any structured-cloneable value
31
+ * @param clientIds the clients to push to, every client
32
+ * (the main process included) when none are given
33
+ * @returns the `clientId` of each client the event was posted to
34
+ */
35
+ sendEvent(data: any, ...clientIds: string[]): string[];
13
36
  /**
14
37
  * Singleton instance
15
38
  *
@@ -3,26 +3,40 @@ import { match } from "ts-pattern";
3
3
  import * as UPM from "../common/index.js";
4
4
  import Tracer from "tracer";
5
5
  import { Future } from "@3fv/prelude-ts";
6
- const log = Tracer.colorConsole();
6
+ const log = Tracer.colorConsole({ level: "info" });
7
+ /**
8
+ * `clientId` of the main process, which is
9
+ * reached through `process.parentPort`
10
+ */
11
+ export const MainClientId = "main";
7
12
  export class UPMNodeProcess {
8
13
  onServiceClientPort(clientId, port) {
9
14
  log.info(`Received new client port for clientId (${clientId})`);
10
- assert(!this.clientPorts_.has(clientId), `port already registered for clientId(${clientId})`);
15
+ // A CLIENT WHICH RECONNECTS (i.e. A RELOADED RENDERER) REPLACES ITS PORT
16
+ const existingPort = this.clientPorts_.get(clientId);
17
+ if (existingPort) {
18
+ log.warn(`Replacing the port registered for clientId (${clientId})`);
19
+ this.clientPorts_.delete(clientId);
20
+ existingPort.close();
21
+ }
22
+ this.clientPorts_.set(clientId, port);
11
23
  port.on("message", (ev) => {
12
24
  var _a, _b;
13
25
  const envelope = ((_b = (_a = ev.data) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : ev.data), { channel, payload } = envelope;
14
- log.info(`Message received on client port (${clientId})`, envelope);
26
+ log.debug(`Message received on client port (${clientId})`, envelope);
15
27
  assert(channel === UPM.IPCChannel.UPMServiceMessage, `Only IPCChannel.UPMServiceMessage are allowed on client ports: ${channel}`);
16
28
  Future.of(this.onServiceMessage(clientId, port, payload))
17
29
  .onFailure(err => log.error(`Unable to handle message`, err));
18
30
  });
19
31
  port.on("close", () => {
20
- this.clientPorts_.delete(clientId);
32
+ if (this.clientPorts_.get(clientId) === port) {
33
+ this.clientPorts_.delete(clientId);
34
+ }
21
35
  });
22
36
  port.start();
23
37
  }
24
38
  async onServiceMessage(clientId, port, payload) {
25
- log.info(`onServiceMessage from (clientId=${clientId})`, payload);
39
+ log.debug(`onServiceMessage from (clientId=${clientId})`, payload);
26
40
  const { messageId, kind, args, eventData, type } = payload;
27
41
  await match(kind)
28
42
  .with(UPM.MessageKind.Request, async () => {
@@ -65,7 +79,7 @@ export class UPMNodeProcess {
65
79
  res = await resPromise;
66
80
  }
67
81
  if (res === true) {
68
- log.info(`Successfully handled (${messageId}) event`);
82
+ log.debug(`Successfully handled (${messageId}) event`);
69
83
  return;
70
84
  }
71
85
  }
@@ -80,10 +94,10 @@ export class UPMNodeProcess {
80
94
  }
81
95
  onMessage(port, message) {
82
96
  var _a, _b;
83
- log.info(`Message received on utilityProcess`, message.data);
97
+ log.debug(`Message received on utilityProcess`, message.data);
84
98
  const { channel, payload } = ((_b = (_a = message.data) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : message.data);
85
99
  match(channel)
86
- .when(c => c === UPM.IPCChannel.UPMServiceMessage, () => this.onServiceMessage("main", port, payload))
100
+ .when(c => c === UPM.IPCChannel.UPMServiceMessage, () => this.onServiceMessage(MainClientId, port, payload))
87
101
  .when(c => c === UPM.IPCChannel.UPMServiceNewClient, () => this.onServiceClientPort(payload.clientId, message.ports[0]))
88
102
  .otherwise(c => {
89
103
  log.error(`Unknown channel (${c})`);
@@ -94,6 +108,7 @@ export class UPMNodeProcess {
94
108
  this.clientPorts_ = new Map();
95
109
  this.requestHandlers_ = new Map();
96
110
  this.eventHandlers_ = Array();
111
+ this.messageIdSeq_ = 0;
97
112
  this.processPort.on("message", (message) => this.onMessage(this.processPort, message));
98
113
  }
99
114
  addRequestHandler(type, handler) {
@@ -104,6 +119,46 @@ export class UPMNodeProcess {
104
119
  log.info(`Registering event handler`);
105
120
  this.eventHandlers_.push(handler);
106
121
  }
122
+ /**
123
+ * The `clientId` of every client which can currently
124
+ * be pushed an event: the main process (`MainClientId`)
125
+ * and each client with an open port
126
+ */
127
+ get clientIds() {
128
+ return [MainClientId, ...this.clientPorts_.keys()];
129
+ }
130
+ /**
131
+ * Push an event from the `utilityProcess` to its clients
132
+ * as `fire-and-forget`; a client receives it through
133
+ * the handlers it registered with `addEventHandler`.
134
+ *
135
+ * @param data any structured-cloneable value
136
+ * @param clientIds the clients to push to, every client
137
+ * (the main process included) when none are given
138
+ * @returns the `clientId` of each client the event was posted to
139
+ */
140
+ sendEvent(data, ...clientIds) {
141
+ const messageId = this.messageIdSeq_++, envelope = {
142
+ channel: UPM.IPCChannel.UPMServiceMessage,
143
+ payload: { messageId, eventData: data, kind: UPM.MessageKind.Event }
144
+ }, targetIds = clientIds.length ? [...new Set(clientIds)] : this.clientIds, sentIds = Array();
145
+ for (const clientId of targetIds) {
146
+ const port = clientId === MainClientId ? this.processPort : this.clientPorts_.get(clientId);
147
+ if (!port) {
148
+ log.warn(`Unable to send event (${messageId}), no port for clientId (${clientId})`);
149
+ continue;
150
+ }
151
+ try {
152
+ port.postMessage(envelope);
153
+ sentIds.push(clientId);
154
+ }
155
+ catch (err) {
156
+ log.error(`Unable to send event (${messageId}) to clientId (${clientId})`, err);
157
+ }
158
+ }
159
+ log.debug(`Sent event (${messageId}) to clients`, sentIds);
160
+ return sentIds;
161
+ }
107
162
  /**
108
163
  * Get singleton instance
109
164
  */
@@ -1 +1 @@
1
- {"version":3,"file":"UPMNodeProcess.js","sourceRoot":"","sources":["../../../src/node/UPMNodeProcess.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"UPMNodeProcess.js","sourceRoot":"","sources":["../../../src/node/UPMNodeProcess.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { Deferred } from "@3fv/deferred";
3
3
  import { ipcRenderer } from "electron";
4
4
  import { assert } from "@3fv/guard";
5
5
  import Tracer from "tracer";
6
- const log = Tracer.colorConsole();
6
+ const log = Tracer.colorConsole({ level: "info" });
7
7
  export class UPMRendererClientFactory {
8
8
  static async createClient(serviceName, clientId = "renderer") {
9
9
  const deferred = new Deferred();
@@ -1 +1 @@
1
- {"version":3,"file":"UPMRendererClientFactory.js","sourceRoot":"","sources":["../../../src/renderer/UPMRendererClientFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACnC,OAAO,MAAM,MAAM,QAAQ,CAAA;AAE3B,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,EAAE,CAAA;AAEjC,MAAM,OAAO,wBAAwB;IAEnC,MAAM,CAAC,KAAK,CAAC,YAAY,CAGvB,WAAmB,EAAE,WAAmB,UAAU;QAClD,MAAM,QAAQ,GAAI,IAAI,QAAQ,EAAuC,CAAA;QAErE,MAAM,WAAW,GAAG,CAAC,EAA6B,EAAE,IAA4C,EAAE,EAAE;YAClG,IAAI,CAAC;gBACH,MAAM,EAAC,WAAW,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAC,GAAG,IAAI,CAAA;gBAEnE,GAAG,CAAC,IAAI,CAAC,2CAA2C,WAAW,oBAAoB,eAAe,aAAa,QAAQ,iBAAiB,YAAY,GAAG,CAAC,CAAA;gBAGxJ,IAAI,WAAW,KAAK,eAAe,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC;oBACjE,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;oBACpC,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBAC1B,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAA;oBAC9D,CAAC;yBAAM,CAAC;wBACN,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;wBACxB,QAAQ,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAA;oBAC7D,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,KAAK,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAA;gBACnC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACtB,CAAC;QACH,CAAC,CAAA;QAED,IAAI,CAAC;YACH,WAAW,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAA;YAC/D,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,mBAAmB,EAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;YAE1E,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAA;YAC5C,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,+CAA+C,CAAC,CAAA;QAC/E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACpB,MAAM,GAAG,CAAA;QACX,CAAC;gBAAS,CAAC;YACT,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAA;QAClE,CAAC;QAED,OAAO,QAAQ,CAAC,OAAO,CAAA;IACzB,CAAC;CAEF;AAED,eAAe,wBAAwB,CAAA"}
1
+ {"version":3,"file":"UPMRendererClientFactory.js","sourceRoot":"","sources":["../../../src/renderer/UPMRendererClientFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACnC,OAAO,MAAM,MAAM,QAAQ,CAAA;AAE3B,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;AAElD,MAAM,OAAO,wBAAwB;IAEnC,MAAM,CAAC,KAAK,CAAC,YAAY,CAGvB,WAAmB,EAAE,WAAmB,UAAU;QAClD,MAAM,QAAQ,GAAI,IAAI,QAAQ,EAAuC,CAAA;QAErE,MAAM,WAAW,GAAG,CAAC,EAA6B,EAAE,IAA4C,EAAE,EAAE;YAClG,IAAI,CAAC;gBACH,MAAM,EAAC,WAAW,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAC,GAAG,IAAI,CAAA;gBAEnE,GAAG,CAAC,IAAI,CAAC,2CAA2C,WAAW,oBAAoB,eAAe,aAAa,QAAQ,iBAAiB,YAAY,GAAG,CAAC,CAAA;gBAGxJ,IAAI,WAAW,KAAK,eAAe,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC;oBACjE,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;oBACpC,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBAC1B,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAA;oBAC9D,CAAC;yBAAM,CAAC;wBACN,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;wBACxB,QAAQ,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAA;oBAC7D,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,KAAK,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAA;gBACnC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACtB,CAAC;QACH,CAAC,CAAA;QAED,IAAI,CAAC;YACH,WAAW,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAA;YAC/D,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,mBAAmB,EAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;YAE1E,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAA;YAC5C,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,EAAE,+CAA+C,CAAC,CAAA;QAC/E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACpB,MAAM,GAAG,CAAA;QACX,CAAC;gBAAS,CAAC;YACT,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAA;QAClE,CAAC;QAED,OAAO,QAAQ,CAAC,OAAO,CAAA;IACzB,CAAC;CAEF;AAED,eAAe,wBAAwB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3fv/electron-utility-process-manager",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "packageManager": "pnpm@12.15.0",
5
5
  "description": "electron-utility-process-manager",
6
6
  "author": "Jonathan Glanz",
@@ -84,11 +84,10 @@
84
84
  "build:watch": "pnpm run build --watch",
85
85
  "build:examples": "pnpm --filter @3fv/electron-utility-process-manager-examples run build",
86
86
  "example": "node ./scripts/run-example.mjs",
87
- "test": "jest --passWithNoTests",
87
+ "test": "jest",
88
88
  "test:watch": "jest --watchAll --no-cache",
89
89
  "test:dev": "run-s build && run-p test:watch build:watch",
90
- "preversion": "pnpm build",
91
- "preversion::with-tests": "pnpm build && pnpm test",
90
+ "preversion": "pnpm build && pnpm test",
92
91
  "release": "gh workflow run release.yml --ref master -f bump=patch"
93
92
  },
94
93
  "publishConfig": {
@@ -105,8 +104,10 @@
105
104
  ],
106
105
  "files": [
107
106
  "lib",
107
+ "!lib/**/*.spec.*",
108
108
  "!lib/**/*.tsbuildinfo",
109
109
  "src",
110
+ "!src/**/*.spec.ts",
110
111
  "README.md"
111
112
  ],
112
113
  "dependencies": {
@@ -1,7 +1,9 @@
1
1
  import { isDefined, isFunction } from "@3fv/guard"
2
2
  import { isMessagePort, isUtilityProcess } from "./guards.js"
3
3
  import Tracer from "tracer"
4
+ import { ClientEventHandlers } from "./events.js"
4
5
  import {
6
+ ClientEventHandler,
5
7
  Defaults,
6
8
  IPCChannel,
7
9
  Message, MessageKind, MessageRequestFn,
@@ -13,7 +15,7 @@ import {
13
15
  } from "./types.js"
14
16
  import { Deferred } from "@3fv/deferred"
15
17
 
16
- const log = Tracer.colorConsole()
18
+ const log = Tracer.colorConsole({ level: "info" })
17
19
 
18
20
  /**
19
21
  * Service client interface maps a `MessageRequestMap`
@@ -46,6 +48,22 @@ export interface IMessageClient<
46
48
  */
47
49
  sendEvent(data: any): void
48
50
 
51
+ /**
52
+ * Register a handler for events pushed by the
53
+ * `utilityProcess` (`UPMNodeProcess.sendEvent`)
54
+ *
55
+ * @param handler
56
+ * @returns a function which removes the handler
57
+ */
58
+ addEventHandler(handler: ClientEventHandler): () => void
59
+
60
+ /**
61
+ * Remove a handler registered with `addEventHandler`
62
+ *
63
+ * @param handler
64
+ */
65
+ removeEventHandler(handler: ClientEventHandler): void
66
+
49
67
  /**
50
68
  * Execute a request, with a response expected
51
69
  *
@@ -105,6 +123,13 @@ export class MessagePortClient<
105
123
  */
106
124
  private readonly pendingMessages_ = new Map<number, PendingRequestMessage>()
107
125
 
126
+ /**
127
+ * Handlers for events pushed by the `utilityProcess`
128
+ *
129
+ * @private
130
+ */
131
+ private readonly eventHandlers_ = new ClientEventHandlers()
132
+
108
133
  /**
109
134
  * Get the next `messageId`
110
135
  *
@@ -159,10 +184,17 @@ export class MessagePortClient<
159
184
  ) as [string, Message<ReqMap, Type>],
160
185
  { messageId, result, error } = payload
161
186
 
187
+ // EVENT PUSHED BY THE `utilityProcess`
188
+ if (payload?.kind === MessageKind.Event) {
189
+ log.debug(`Event received (clientId=${this.clientId},messageId=${messageId})`)
190
+ this.eventHandlers_.dispatch(payload.eventData)
191
+ return
192
+ }
193
+
162
194
  try {
163
195
  const pending = this.pendingMessages_.get(messageId)
164
196
  if (!pending) {
165
- log.info(`Unable to find pending record ${messageId}`)
197
+ log.debug(`Unable to find pending record ${messageId}`)
166
198
  return
167
199
  }
168
200
 
@@ -194,6 +226,14 @@ export class MessagePortClient<
194
226
  this.port.postMessage(payload)
195
227
  }
196
228
 
229
+ addEventHandler(handler: ClientEventHandler): () => void {
230
+ return this.eventHandlers_.add(handler)
231
+ }
232
+
233
+ removeEventHandler(handler: ClientEventHandler): void {
234
+ this.eventHandlers_.remove(handler)
235
+ }
236
+
197
237
  async executeRequest<
198
238
  Type extends MType,
199
239
  R extends MessageRequestReturnType<ReqMap, Type> = MessageRequestReturnType<ReqMap, Type>
@@ -0,0 +1,43 @@
1
+ import Tracer from "tracer"
2
+ import type { ClientEventHandler } from "./types.js"
3
+
4
+ const log = Tracer.colorConsole({ level: "info" })
5
+
6
+ /**
7
+ * Holds the handlers a client registered for
8
+ * events pushed by the `utilityProcess`
9
+ */
10
+ export class ClientEventHandlers {
11
+ private readonly handlers_ = new Set<ClientEventHandler>()
12
+
13
+ /**
14
+ * Register a handler
15
+ *
16
+ * @param handler
17
+ * @returns a function which removes the handler
18
+ */
19
+ add(handler: ClientEventHandler): () => void {
20
+ this.handlers_.add(handler)
21
+ return () => this.remove(handler)
22
+ }
23
+
24
+ remove(handler: ClientEventHandler): void {
25
+ this.handlers_.delete(handler)
26
+ }
27
+
28
+ /**
29
+ * Call every handler with the event data, a handler
30
+ * which throws does not stop the others
31
+ *
32
+ * @param data
33
+ */
34
+ dispatch(data: any): void {
35
+ for (const handler of [...this.handlers_]) {
36
+ try {
37
+ handler(data)
38
+ } catch (err) {
39
+ log.error(`Event handler failed`, err)
40
+ }
41
+ }
42
+ }
43
+ }
@@ -1,3 +1,4 @@
1
1
  export * from "./types.js"
2
2
  export * from "./guards.js"
3
+ export * from "./events.js"
3
4
  export * from "./clients.js"
@@ -78,6 +78,12 @@ export interface PendingRequestMessage<
78
78
  */
79
79
  export type EventHandler = (clientId: string, port: Port, payload: any) => boolean
80
80
 
81
+ /**
82
+ * Handles an event pushed by the `utilityProcess`
83
+ * to a client (`UPMNodeProcess.sendEvent`)
84
+ */
85
+ export type ClientEventHandler = (data: any) => void
86
+
81
87
  /**
82
88
  * Port of any kind
83
89
  */
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  app, MessageChannelMain, MessagePortMain, utilityProcess, UtilityProcess
3
3
  } from "electron"
4
- import { assert, getValue, isNumber, isString } from "@3fv/guard"
4
+ import { assert, getValue, isDefined, isNumber, isString } from "@3fv/guard"
5
5
  import Fs from "fs"
6
6
  import { Deferred } from "@3fv/deferred"
7
7
  import * as UPM from "../common/index.js"
@@ -12,7 +12,7 @@ import Tracer from "tracer"
12
12
  import { asOption } from "@3fv/prelude-ts"
13
13
  import { match } from "ts-pattern"
14
14
 
15
- const log = Tracer.colorConsole()
15
+ const log = Tracer.colorConsole({ level: "info" })
16
16
  const isNotEmpty = negate(isEmpty)
17
17
 
18
18
  /**
@@ -46,6 +46,26 @@ export class UPMMainService<ReqMap extends UPM.MessageRequestMap = any, MType ex
46
46
  port.postMessage(payload)
47
47
  }
48
48
 
49
+ /**
50
+ * Register a handler for events the service pushes to
51
+ * the main process (`UPMNodeProcess.sendEvent`)
52
+ *
53
+ * @param handler
54
+ * @returns a function which removes the handler
55
+ */
56
+ addEventHandler(handler:UPM.ClientEventHandler):() => void {
57
+ return this.eventHandlers_.add(handler)
58
+ }
59
+
60
+ /**
61
+ * Remove a handler registered with `addEventHandler`
62
+ *
63
+ * @param handler
64
+ */
65
+ removeEventHandler(handler:UPM.ClientEventHandler):void {
66
+ this.eventHandlers_.remove(handler)
67
+ }
68
+
49
69
  /**
50
70
  * Send request
51
71
  *
@@ -130,6 +150,8 @@ export class UPMMainService<ReqMap extends UPM.MessageRequestMap = any, MType ex
130
150
 
131
151
  private pendingMessages_ = new Map<number, UPM.PendingRequestMessage>()
132
152
 
153
+ private eventHandlers_ = new UPM.ClientEventHandlers()
154
+
133
155
  private messageChannels_ = new Map<string, MessageChannelMain>()
134
156
 
135
157
  private onServiceExit(code:number) {
@@ -271,20 +293,30 @@ export class UPMMainService<ReqMap extends UPM.MessageRequestMap = any, MType ex
271
293
  }
272
294
 
273
295
  /**
274
- * OnMessage really only handles `results` from
275
- * `executeRequest(...)`; this may be expanded in
276
- * the future
296
+ * Handles what the service posts on its parent port:
297
+ * `results` of `executeRequest(...)` & events pushed
298
+ * with `UPMNodeProcess.sendEvent(...)`
277
299
  *
278
- * @param payload
300
+ * @param payloadOrEnvelope the service posts a `NodeEnvelope`
279
301
  * @private
280
302
  */
281
- private onMessage<Type extends MType = any>(payload:UPM.Message<ReqMap, Type>) {
282
- const { messageId, result, error } = payload
303
+ private onMessage<Type extends MType = any>(payloadOrEnvelope:UPM.Message<ReqMap, Type> | UPM.NodeMessage<ReqMap, Type>) {
304
+ const payload = (
305
+ isDefined(payloadOrEnvelope?.["payload"]) ? payloadOrEnvelope["payload"] : payloadOrEnvelope
306
+ ) as UPM.Message<ReqMap, Type>,
307
+ { messageId, result, error } = payload
308
+
309
+ // EVENT PUSHED BY THE SERVICE
310
+ if (payload?.kind === UPM.MessageKind.Event) {
311
+ log.debug(`Event received from service (${this.serviceName},messageId=${messageId})`)
312
+ this.eventHandlers_.dispatch(payload.eventData)
313
+ return
314
+ }
283
315
 
284
316
  try {
285
317
  const pending = this.pendingMessages_.get(messageId)
286
318
  if (!pending) {
287
- log.info(`Unable to find pending record ${messageId}`)
319
+ log.debug(`Unable to find pending record ${messageId}`)
288
320
  return
289
321
  }
290
322
 
@@ -6,7 +6,7 @@ import UPMMainService from "./UPMMainService.js"
6
6
 
7
7
  import Tracer from "tracer"
8
8
 
9
- const log = Tracer.colorConsole()
9
+ const log = Tracer.colorConsole({ level: "info" })
10
10
 
11
11
 
12
12
 
@@ -4,7 +4,13 @@ import * as UPM from "../common/index.js"
4
4
  import Tracer from "tracer"
5
5
  import { Future } from "@3fv/prelude-ts"
6
6
 
7
- const log = Tracer.colorConsole()
7
+ const log = Tracer.colorConsole({ level: "info" })
8
+
9
+ /**
10
+ * `clientId` of the main process, which is
11
+ * reached through `process.parentPort`
12
+ */
13
+ export const MainClientId = "main"
8
14
 
9
15
  export class UPMNodeProcess
10
16
  {
@@ -16,16 +22,26 @@ export class UPMNodeProcess
16
22
 
17
23
  private readonly eventHandlers_ = Array<UPM.EventHandler>()
18
24
 
25
+ private messageIdSeq_ = 0
26
+
19
27
  private onServiceClientPort(clientId: string, port: Electron.MessagePortMain) {
20
28
  log.info(`Received new client port for clientId (${clientId})`)
21
- assert(!this.clientPorts_.has(clientId), `port already registered for clientId(${clientId})`)
29
+
30
+ // A CLIENT WHICH RECONNECTS (i.e. A RELOADED RENDERER) REPLACES ITS PORT
31
+ const existingPort = this.clientPorts_.get(clientId)
32
+ if (existingPort) {
33
+ log.warn(`Replacing the port registered for clientId (${clientId})`)
34
+ this.clientPorts_.delete(clientId)
35
+ existingPort.close()
36
+ }
37
+ this.clientPorts_.set(clientId, port)
22
38
 
23
39
  port.on("message", (ev: Electron.MessageEvent) => {
24
40
  const envelope = (
25
41
  ev.data?.message ?? ev.data
26
42
  ) as UPM.NodeEnvelope,
27
43
  { channel, payload } = envelope
28
- log.info(`Message received on client port (${clientId})`, envelope)
44
+ log.debug(`Message received on client port (${clientId})`, envelope)
29
45
 
30
46
  assert(
31
47
  channel === UPM.IPCChannel.UPMServiceMessage,
@@ -36,14 +52,16 @@ export class UPMNodeProcess
36
52
  })
37
53
 
38
54
  port.on("close", () => {
39
- this.clientPorts_.delete(clientId)
55
+ if (this.clientPorts_.get(clientId) === port) {
56
+ this.clientPorts_.delete(clientId)
57
+ }
40
58
  })
41
59
 
42
60
  port.start()
43
61
  }
44
62
 
45
63
  private async onServiceMessage(clientId: string, port: UPM.Port, payload: UPM.Message<any, any>) {
46
- log.info(`onServiceMessage from (clientId=${clientId})`, payload)
64
+ log.debug(`onServiceMessage from (clientId=${clientId})`, payload)
47
65
  const { messageId, kind, args, eventData, type } = payload
48
66
  await match(kind)
49
67
  .with(UPM.MessageKind.Request, async () => {
@@ -92,7 +110,7 @@ export class UPMNodeProcess
92
110
  }
93
111
 
94
112
  if (res === true) {
95
- log.info(`Successfully handled (${messageId}) event`)
113
+ log.debug(`Successfully handled (${messageId}) event`)
96
114
  return
97
115
  }
98
116
  }
@@ -107,12 +125,12 @@ export class UPMNodeProcess
107
125
  }
108
126
 
109
127
  private onMessage(port: UPM.Port, message: Electron.MessageEvent) {
110
- log.info(`Message received on utilityProcess`, message.data)
128
+ log.debug(`Message received on utilityProcess`, message.data)
111
129
  const { channel, payload } = (
112
130
  message.data?.message ?? message.data
113
131
  ) as UPM.NodeEnvelope
114
132
  match(channel)
115
- .when(c => c === UPM.IPCChannel.UPMServiceMessage, () => this.onServiceMessage("main", port, payload))
133
+ .when(c => c === UPM.IPCChannel.UPMServiceMessage, () => this.onServiceMessage(MainClientId, port, payload))
116
134
  .when(
117
135
  c => c === UPM.IPCChannel.UPMServiceNewClient,
118
136
  () => this.onServiceClientPort(payload.clientId, message.ports[0])
@@ -140,6 +158,53 @@ export class UPMNodeProcess
140
158
  this.eventHandlers_.push(handler)
141
159
  }
142
160
 
161
+ /**
162
+ * The `clientId` of every client which can currently
163
+ * be pushed an event: the main process (`MainClientId`)
164
+ * and each client with an open port
165
+ */
166
+ get clientIds(): string[] {
167
+ return [MainClientId, ...this.clientPorts_.keys()]
168
+ }
169
+
170
+ /**
171
+ * Push an event from the `utilityProcess` to its clients
172
+ * as `fire-and-forget`; a client receives it through
173
+ * the handlers it registered with `addEventHandler`.
174
+ *
175
+ * @param data any structured-cloneable value
176
+ * @param clientIds the clients to push to, every client
177
+ * (the main process included) when none are given
178
+ * @returns the `clientId` of each client the event was posted to
179
+ */
180
+ sendEvent(data: any, ...clientIds: string[]): string[] {
181
+ const messageId = this.messageIdSeq_++,
182
+ envelope: UPM.NodeEnvelope = {
183
+ channel: UPM.IPCChannel.UPMServiceMessage,
184
+ payload: { messageId, eventData: data, kind: UPM.MessageKind.Event }
185
+ },
186
+ targetIds = clientIds.length ? [...new Set(clientIds)] : this.clientIds,
187
+ sentIds = Array<string>()
188
+
189
+ for (const clientId of targetIds) {
190
+ const port = clientId === MainClientId ? this.processPort : this.clientPorts_.get(clientId)
191
+ if (!port) {
192
+ log.warn(`Unable to send event (${messageId}), no port for clientId (${clientId})`)
193
+ continue
194
+ }
195
+
196
+ try {
197
+ port.postMessage(envelope)
198
+ sentIds.push(clientId)
199
+ } catch (err) {
200
+ log.error(`Unable to send event (${messageId}) to clientId (${clientId})`, err)
201
+ }
202
+ }
203
+
204
+ log.debug(`Sent event (${messageId}) to clients`, sentIds)
205
+ return sentIds
206
+ }
207
+
143
208
  /**
144
209
  * Singleton instance
145
210
  *
@@ -4,7 +4,7 @@ import { ipcRenderer } from "electron"
4
4
  import { assert } from "@3fv/guard"
5
5
  import Tracer from "tracer"
6
6
 
7
- const log = Tracer.colorConsole()
7
+ const log = Tracer.colorConsole({ level: "info" })
8
8
 
9
9
  export class UPMRendererClientFactory {
10
10