@3fv/electron-utility-process-manager 1.0.6 → 1.0.8
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 +53 -2
- package/lib/cjs/index.js +17 -7
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/main/UPMMainService.js +24 -12
- package/lib/cjs/main/UPMMainService.js.map +1 -1
- package/lib/cjs/main/UPMMainServiceManager.js +17 -7
- package/lib/cjs/main/UPMMainServiceManager.js.map +1 -1
- package/lib/cjs/node/UPMNodeProcess.js +17 -7
- package/lib/cjs/node/UPMNodeProcess.js.map +1 -1
- package/lib/cjs/package.json +1 -0
- package/lib/cjs/renderer/UPMRendererClientFactory.js +17 -7
- package/lib/cjs/renderer/UPMRendererClientFactory.js.map +1 -1
- package/lib/mjs/main/UPMMainService.js +5 -3
- package/lib/mjs/main/UPMMainService.js.map +1 -1
- package/lib/mjs/package.json +1 -0
- package/package.json +81 -43
- package/src/common/clients.ts +276 -0
- package/src/common/guards.ts +11 -0
- package/src/common/index.ts +3 -0
- package/src/common/types.ts +106 -0
- package/src/index.ts +8 -0
- package/src/main/UPMMainService.ts +454 -0
- package/src/main/UPMMainServiceManager.ts +178 -0
- package/src/main/index.ts +2 -0
- package/src/node/UPMNodeProcess.ts +164 -0
- package/src/node/index.ts +3 -0
- package/src/renderer/UPMRendererClientFactory.ts +57 -0
- package/src/renderer/index.ts +4 -0
- package/.prettierrc.js +0 -14
- package/examples/lib-examples/complex/complex-main.d.ts +0 -1
- package/examples/lib-examples/complex/complex-main.js +0 -108
- package/examples/lib-examples/complex/complex-main.js.map +0 -1
- package/examples/lib-examples/complex/complex-node.d.ts +0 -1
- package/examples/lib-examples/complex/complex-node.js +0 -17
- package/examples/lib-examples/complex/complex-node.js.map +0 -1
- package/examples/lib-examples/complex/complex-renderer.d.ts +0 -1
- package/examples/lib-examples/complex/complex-renderer.js +0 -18
- package/examples/lib-examples/complex/complex-renderer.js.map +0 -1
- package/examples/lib-examples/complex/complex-types.d.ts +0 -3
- package/examples/lib-examples/complex/complex-types.js +0 -3
- package/examples/lib-examples/complex/complex-types.js.map +0 -1
- package/examples/lib-examples/simple/simple-main.d.ts +0 -1
- package/examples/lib-examples/simple/simple-main.js +0 -113
- package/examples/lib-examples/simple/simple-main.js.map +0 -1
- package/examples/lib-examples/simple/simple-node.d.ts +0 -1
- package/examples/lib-examples/simple/simple-node.js +0 -17
- package/examples/lib-examples/simple/simple-node.js.map +0 -1
- package/examples/lib-examples/simple/simple-renderer.d.ts +0 -1
- package/examples/lib-examples/simple/simple-renderer.js +0 -17
- package/examples/lib-examples/simple/simple-renderer.js.map +0 -1
- package/jest.config.js +0 -20
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@3fv/electron-utility-process-manager",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
|
+
"packageManager": "pnpm@12.15.0",
|
|
4
5
|
"description": "electron-utility-process-manager",
|
|
5
6
|
"author": "Jonathan Glanz",
|
|
6
7
|
"license": "MIT",
|
|
@@ -11,54 +12,91 @@
|
|
|
11
12
|
"type": "commonjs",
|
|
12
13
|
"main": "./lib/cjs/index.js",
|
|
13
14
|
"module": "./lib/mjs/index.js",
|
|
14
|
-
"types": "./lib/
|
|
15
|
+
"types": "./lib/cjs/index.d.ts",
|
|
15
16
|
"exports": {
|
|
16
17
|
".": {
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
18
|
+
"node": {
|
|
19
|
+
"types": "./lib/cjs/index.d.ts",
|
|
20
|
+
"default": "./lib/cjs/index.js"
|
|
21
|
+
},
|
|
22
|
+
"browser": {
|
|
23
|
+
"types": "./lib/cjs/index.d.ts",
|
|
24
|
+
"default": "./lib/cjs/index.js"
|
|
25
|
+
},
|
|
26
|
+
"import": {
|
|
27
|
+
"types": "./lib/mjs/index.d.ts",
|
|
28
|
+
"default": "./lib/mjs/index.js"
|
|
29
|
+
},
|
|
30
|
+
"require": {
|
|
31
|
+
"types": "./lib/cjs/index.d.ts",
|
|
32
|
+
"default": "./lib/cjs/index.js"
|
|
33
|
+
}
|
|
22
34
|
},
|
|
23
35
|
"./main": {
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
"node": {
|
|
37
|
+
"types": "./lib/cjs/main/index.d.ts",
|
|
38
|
+
"default": "./lib/cjs/main/index.js"
|
|
39
|
+
},
|
|
40
|
+
"import": {
|
|
41
|
+
"types": "./lib/mjs/main/index.d.ts",
|
|
42
|
+
"default": "./lib/mjs/main/index.js"
|
|
43
|
+
},
|
|
44
|
+
"require": {
|
|
45
|
+
"types": "./lib/cjs/main/index.d.ts",
|
|
46
|
+
"default": "./lib/cjs/main/index.js"
|
|
47
|
+
}
|
|
28
48
|
},
|
|
29
49
|
"./renderer": {
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
50
|
+
"node": {
|
|
51
|
+
"types": "./lib/cjs/renderer/index.d.ts",
|
|
52
|
+
"default": "./lib/cjs/renderer/index.js"
|
|
53
|
+
},
|
|
54
|
+
"browser": {
|
|
55
|
+
"types": "./lib/cjs/renderer/index.d.ts",
|
|
56
|
+
"default": "./lib/cjs/renderer/index.js"
|
|
57
|
+
},
|
|
58
|
+
"import": {
|
|
59
|
+
"types": "./lib/mjs/renderer/index.d.ts",
|
|
60
|
+
"default": "./lib/mjs/renderer/index.js"
|
|
61
|
+
},
|
|
62
|
+
"require": {
|
|
63
|
+
"types": "./lib/cjs/renderer/index.d.ts",
|
|
64
|
+
"default": "./lib/cjs/renderer/index.js"
|
|
65
|
+
}
|
|
35
66
|
},
|
|
36
67
|
"./node": {
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
68
|
+
"node": {
|
|
69
|
+
"types": "./lib/cjs/node/index.d.ts",
|
|
70
|
+
"default": "./lib/cjs/node/index.js"
|
|
71
|
+
},
|
|
72
|
+
"import": {
|
|
73
|
+
"types": "./lib/mjs/node/index.d.ts",
|
|
74
|
+
"default": "./lib/mjs/node/index.js"
|
|
75
|
+
},
|
|
76
|
+
"require": {
|
|
77
|
+
"types": "./lib/cjs/node/index.d.ts",
|
|
78
|
+
"default": "./lib/cjs/node/index.js"
|
|
79
|
+
}
|
|
41
80
|
}
|
|
42
81
|
},
|
|
43
82
|
"scripts": {
|
|
44
83
|
"build": "node ./scripts/build.mjs",
|
|
45
|
-
"build:
|
|
46
|
-
"build:
|
|
47
|
-
"
|
|
48
|
-
"test
|
|
84
|
+
"build:watch": "pnpm run build --watch",
|
|
85
|
+
"build:examples": "pnpm --filter @3fv/electron-utility-process-manager-examples run build",
|
|
86
|
+
"example": "node ./scripts/run-example.mjs",
|
|
87
|
+
"test": "jest --passWithNoTests",
|
|
88
|
+
"test:watch": "jest --watchAll --no-cache",
|
|
49
89
|
"test:dev": "run-s build && run-p test:watch build:watch",
|
|
50
|
-
"preversion": "
|
|
51
|
-
"preversion::with-tests": "
|
|
52
|
-
"
|
|
53
|
-
"postversion": "node ./scripts/release.mjs latest",
|
|
54
|
-
"release": "yarn build && yarn version --patch --non-interactive"
|
|
90
|
+
"preversion": "pnpm build",
|
|
91
|
+
"preversion::with-tests": "pnpm build && pnpm test",
|
|
92
|
+
"release": "gh workflow run release.yml --ref master -f bump=patch"
|
|
55
93
|
},
|
|
56
94
|
"publishConfig": {
|
|
57
95
|
"access": "public"
|
|
58
96
|
},
|
|
59
97
|
"repository": {
|
|
60
98
|
"type": "git",
|
|
61
|
-
"url": "https://github.com/3fv/electron-utility-process-manager.git"
|
|
99
|
+
"url": "git+https://github.com/3fv/electron-utility-process-manager.git"
|
|
62
100
|
},
|
|
63
101
|
"keywords": [
|
|
64
102
|
"typescript",
|
|
@@ -66,15 +104,15 @@
|
|
|
66
104
|
"proxy"
|
|
67
105
|
],
|
|
68
106
|
"files": [
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
107
|
+
"lib",
|
|
108
|
+
"!lib/**/*.tsbuildinfo",
|
|
109
|
+
"src",
|
|
110
|
+
"README.md"
|
|
73
111
|
],
|
|
74
112
|
"dependencies": {
|
|
75
|
-
"@3fv/deferred": "^1.2.
|
|
76
|
-
"@3fv/guard": "^1.4.
|
|
77
|
-
"@3fv/prelude-ts": "^0.8.
|
|
113
|
+
"@3fv/deferred": "^1.2.17",
|
|
114
|
+
"@3fv/guard": "^1.4.41",
|
|
115
|
+
"@3fv/prelude-ts": "^0.8.45",
|
|
78
116
|
"chalk": "^5.3.0",
|
|
79
117
|
"debug": "^4.3.6",
|
|
80
118
|
"lodash": "^4.17.21",
|
|
@@ -82,20 +120,20 @@
|
|
|
82
120
|
"ts-pattern": "^5.5.0"
|
|
83
121
|
},
|
|
84
122
|
"peerDependencies": {
|
|
85
|
-
"electron": "
|
|
123
|
+
"electron": ">=31"
|
|
86
124
|
},
|
|
87
125
|
"devDependencies": {
|
|
88
126
|
"@faker-js/faker": "^8.4.1",
|
|
89
127
|
"@types/chalk": "^2.2.0",
|
|
90
128
|
"@types/debug": "^4.1.12",
|
|
91
|
-
"@types/jest": "^
|
|
129
|
+
"@types/jest": "^30.0.0",
|
|
92
130
|
"@types/lodash": "^4.17.7",
|
|
93
131
|
"@types/node": "^22.9.0",
|
|
94
132
|
"@types/shelljs": "^0.8.15",
|
|
95
133
|
"cross-env": "^7.0.3",
|
|
96
|
-
"electron": "
|
|
134
|
+
"electron": "45.3.2",
|
|
97
135
|
"electronmon": "^2.0.3",
|
|
98
|
-
"jest": "^
|
|
136
|
+
"jest": "^30.5.2",
|
|
99
137
|
"lodash": "^4.17.21",
|
|
100
138
|
"npm-run-all": "^4.1.5",
|
|
101
139
|
"prettier": "^3.3.3",
|
|
@@ -103,9 +141,9 @@
|
|
|
103
141
|
"shelljs": "^0.8.5",
|
|
104
142
|
"source-map-support": "^0.5.21",
|
|
105
143
|
"supports-color": "^9.4.0",
|
|
106
|
-
"ts-jest": "^29.
|
|
144
|
+
"ts-jest": "^29.4.15",
|
|
107
145
|
"ts-node": "^10.9.2",
|
|
108
|
-
"typescript": "^
|
|
146
|
+
"typescript": "^6.0.3",
|
|
109
147
|
"zx": "^8.1.4"
|
|
110
148
|
}
|
|
111
149
|
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { isDefined, isFunction } from "@3fv/guard"
|
|
2
|
+
import { isMessagePort, isUtilityProcess } from "./guards.js"
|
|
3
|
+
import Tracer from "tracer"
|
|
4
|
+
import {
|
|
5
|
+
Defaults,
|
|
6
|
+
IPCChannel,
|
|
7
|
+
Message, MessageKind, MessageRequestFn,
|
|
8
|
+
MessageRequestMap,
|
|
9
|
+
MessageRequestNames,
|
|
10
|
+
MessageRequestParams,
|
|
11
|
+
MessageRequestReturnType, NodeEnvelope, NodeMessage,
|
|
12
|
+
PendingRequestMessage, Port
|
|
13
|
+
} from "./types.js"
|
|
14
|
+
import { Deferred } from "@3fv/deferred"
|
|
15
|
+
|
|
16
|
+
const log = Tracer.colorConsole()
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Service client interface maps a `MessageRequestMap`
|
|
20
|
+
* to its functional methods
|
|
21
|
+
*/
|
|
22
|
+
export type IServiceClient<
|
|
23
|
+
ReqMap extends MessageRequestMap,
|
|
24
|
+
MType extends MessageRequestNames<ReqMap> = MessageRequestNames<ReqMap>
|
|
25
|
+
> = { [Type in MType]: MessageRequestFn<ReqMap, Type> }
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Messaging client interface
|
|
29
|
+
*/
|
|
30
|
+
export interface IMessageClient<
|
|
31
|
+
ReqMap extends MessageRequestMap,
|
|
32
|
+
MType extends MessageRequestNames<ReqMap> = MessageRequestNames<ReqMap>
|
|
33
|
+
>
|
|
34
|
+
{
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the clientId for the underlying port
|
|
38
|
+
*/
|
|
39
|
+
get clientId(): string
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A very low level function for sending `any` data
|
|
43
|
+
* to the `utilityProcess`
|
|
44
|
+
*
|
|
45
|
+
* @param data
|
|
46
|
+
*/
|
|
47
|
+
sendEvent(data: any): void
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Execute a request, with a response expected
|
|
51
|
+
*
|
|
52
|
+
* @param type
|
|
53
|
+
* @param args determined by the typed `MessageRequestMap` & `Type` parameters
|
|
54
|
+
*/
|
|
55
|
+
executeRequest<
|
|
56
|
+
Type extends MType,
|
|
57
|
+
R extends MessageRequestReturnType<ReqMap, Type> = MessageRequestReturnType<ReqMap, Type>
|
|
58
|
+
>(
|
|
59
|
+
type: Type,
|
|
60
|
+
...args: MessageRequestParams<ReqMap, Type>
|
|
61
|
+
): Promise<R>
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Close the message client
|
|
65
|
+
*/
|
|
66
|
+
close(): void
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Resolve when client is ready to use
|
|
70
|
+
*/
|
|
71
|
+
whenReady(): Promise<IMessageClient<ReqMap, MType>>
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get service client from message client
|
|
75
|
+
*/
|
|
76
|
+
getServiceClient(): IServiceClient<ReqMap, MType>
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Generic `MessagePort` client implementation, which
|
|
81
|
+
* is functional in any process.
|
|
82
|
+
*
|
|
83
|
+
* > NOTE: Aside from the main & renderer processes,
|
|
84
|
+
* > an `utilityProcess` could potentially also
|
|
85
|
+
* > use this client to communicate with a separate
|
|
86
|
+
* > `utilityProcess`
|
|
87
|
+
*/
|
|
88
|
+
export class MessagePortClient<
|
|
89
|
+
ReqMap extends MessageRequestMap,
|
|
90
|
+
MType extends MessageRequestNames<ReqMap> = MessageRequestNames<ReqMap>
|
|
91
|
+
> implements IMessageClient<ReqMap, MType> {
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Used to generate IDS
|
|
95
|
+
*
|
|
96
|
+
* @private
|
|
97
|
+
*/
|
|
98
|
+
private idSequence_: number = 0
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A `Map` responsible for mapping `messageId`
|
|
102
|
+
* values to a `PendingRequestMessage`
|
|
103
|
+
*
|
|
104
|
+
* @private
|
|
105
|
+
*/
|
|
106
|
+
private readonly pendingMessages_ = new Map<number, PendingRequestMessage>()
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get the next `messageId`
|
|
110
|
+
*
|
|
111
|
+
* @returns {number}
|
|
112
|
+
* @private
|
|
113
|
+
*/
|
|
114
|
+
private generateMessageId(): number {
|
|
115
|
+
return this.idSequence_++
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Remove a pending message mapping based on supplied ID
|
|
120
|
+
*
|
|
121
|
+
* @param {number} messageId
|
|
122
|
+
* @private
|
|
123
|
+
*/
|
|
124
|
+
private removePendingMessage(messageId: number): void {
|
|
125
|
+
const pending = this.pendingMessages_.get(messageId)
|
|
126
|
+
if (!pending) {
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
clearTimeout(pending.timeoutId)
|
|
130
|
+
if (!pending.deferred.isSettled()) {
|
|
131
|
+
pending.deferred.reject("The pending message is being removed before promise is settled")
|
|
132
|
+
}
|
|
133
|
+
this.pendingMessages_.delete(messageId)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Handle messages posted by the `utilityProcess` via
|
|
138
|
+
* either `process.parentPort` or a specific `MessagePort`
|
|
139
|
+
*
|
|
140
|
+
* @param payloadOrEnvelopeOrData
|
|
141
|
+
* @private
|
|
142
|
+
*/
|
|
143
|
+
private onMessage<Type extends MType = any>(payloadOrEnvelopeOrData: any): void {
|
|
144
|
+
const
|
|
145
|
+
// FIND THE `Message | NodeMessage` DEPENDING
|
|
146
|
+
// ON THE PROCESS AND TRANSPORT
|
|
147
|
+
payloadOrEnvelope = (
|
|
148
|
+
isDefined(payloadOrEnvelopeOrData?.["data"]) ?
|
|
149
|
+
payloadOrEnvelopeOrData["data"] :
|
|
150
|
+
payloadOrEnvelopeOrData
|
|
151
|
+
) as Message<ReqMap, Type> | NodeMessage<ReqMap, Type>,
|
|
152
|
+
|
|
153
|
+
// FIND THE CHANNEL & PAYLOAD DEPENDING ON THE PROCESS & TRANSPORT
|
|
154
|
+
[channel, payload] =
|
|
155
|
+
(
|
|
156
|
+
isDefined(payloadOrEnvelope?.["payload"]) ?
|
|
157
|
+
[payloadOrEnvelope["channel"] ?? IPCChannel.UPMServiceMessage, payloadOrEnvelope["payload"]] :
|
|
158
|
+
[IPCChannel.UPMServiceMessage, payloadOrEnvelope]
|
|
159
|
+
) as [string, Message<ReqMap, Type>],
|
|
160
|
+
{ messageId, result, error } = payload
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const pending = this.pendingMessages_.get(messageId)
|
|
164
|
+
if (!pending) {
|
|
165
|
+
log.info(`Unable to find pending record ${messageId}`)
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (pending.deferred.isSettled()) {
|
|
170
|
+
log.error(`Already settled record ${messageId}`)
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!!error) {
|
|
175
|
+
log.error(error)
|
|
176
|
+
pending.deferred.reject(new Error(error))
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
pending.deferred.resolve(result)
|
|
180
|
+
this.removePendingMessage(messageId)
|
|
181
|
+
} catch (err) {
|
|
182
|
+
log.error(`Failed to handle message`, err)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
sendEvent(data: any): void {
|
|
188
|
+
const messageId = this.generateMessageId()
|
|
189
|
+
const payload: NodeEnvelope = {
|
|
190
|
+
channel: IPCChannel.UPMServiceMessage,
|
|
191
|
+
payload: { messageId, eventData: data, kind: MessageKind.Event }
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this.port.postMessage(payload)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async executeRequest<
|
|
198
|
+
Type extends MType,
|
|
199
|
+
R extends MessageRequestReturnType<ReqMap, Type> = MessageRequestReturnType<ReqMap, Type>
|
|
200
|
+
>(
|
|
201
|
+
type: Type,
|
|
202
|
+
...args: MessageRequestParams<ReqMap, Type>
|
|
203
|
+
): Promise<R> {
|
|
204
|
+
const messageId = this.generateMessageId(),
|
|
205
|
+
pending: PendingRequestMessage<ReqMap, Type, R> = {
|
|
206
|
+
deferred: new Deferred<R>(),
|
|
207
|
+
messageId,
|
|
208
|
+
timeoutId: setTimeout(() => this.removePendingMessage(messageId), Defaults.RequestTimeout)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this.pendingMessages_.set(messageId, pending)
|
|
212
|
+
const payload: NodeMessage<ReqMap, Type> = {
|
|
213
|
+
channel: IPCChannel.UPMServiceMessage,
|
|
214
|
+
payload: { type, messageId, args, kind: MessageKind.Request }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
this.port.postMessage(payload)
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
const result = await pending.deferred.promise
|
|
221
|
+
log.debug(`Completed utility request (messageId=${messageId})`)
|
|
222
|
+
return result
|
|
223
|
+
} catch (err) {
|
|
224
|
+
log.error(`Failed utility request (messageId=${messageId})`, err)
|
|
225
|
+
throw err
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
close(): void {
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
whenReady(): Promise<this> {
|
|
233
|
+
return Promise.resolve(this)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Return a simple proxy facade wrapping
|
|
238
|
+
* `executeRequest` into individual methods
|
|
239
|
+
*/
|
|
240
|
+
getServiceClient(): IServiceClient<ReqMap, MType> {
|
|
241
|
+
return new Proxy<IServiceClient<ReqMap, MType>>({} as any, {
|
|
242
|
+
get: (_target, prop) => {
|
|
243
|
+
if (prop === "then") {
|
|
244
|
+
return undefined
|
|
245
|
+
}
|
|
246
|
+
return (...args: any[]) => this.whenReady()
|
|
247
|
+
.then(client => client.executeRequest(prop as MType, ...args as any))
|
|
248
|
+
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Create a new `MessagePortClient`
|
|
255
|
+
*
|
|
256
|
+
* @param clientId
|
|
257
|
+
* @param port
|
|
258
|
+
*/
|
|
259
|
+
constructor(readonly clientId: string, readonly port: Port) {
|
|
260
|
+
if (isMessagePort(port) || isUtilityProcess(port)) {
|
|
261
|
+
if (isFunction(port?.["addEventListener"])) {
|
|
262
|
+
;(
|
|
263
|
+
port as any
|
|
264
|
+
).addEventListener("message", this.onMessage.bind(this))
|
|
265
|
+
} else {
|
|
266
|
+
port.on("message", this.onMessage.bind(this))
|
|
267
|
+
}
|
|
268
|
+
if (isMessagePort(port))
|
|
269
|
+
port.start()
|
|
270
|
+
} else {
|
|
271
|
+
log.error("Invalid port", port)
|
|
272
|
+
throw new Error("Invalid port")
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
}
|
|
276
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { UtilityProcess } from "electron"
|
|
2
|
+
import { isFunction } from "@3fv/guard"
|
|
3
|
+
import { Port } from "./types.js"
|
|
4
|
+
|
|
5
|
+
export function isMessagePort(port: Port): port is Electron.MessagePortMain {
|
|
6
|
+
return isFunction(port?.["close"])
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isUtilityProcess(port: Port): port is UtilityProcess {
|
|
10
|
+
return !!port && !isMessagePort(port)
|
|
11
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { UtilityProcess } from "electron"
|
|
2
|
+
import { Deferred } from "@3fv/deferred"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export const Defaults = { RequestTimeout: 120000 }
|
|
6
|
+
|
|
7
|
+
export enum MessageKind {
|
|
8
|
+
Event = "Event",
|
|
9
|
+
Request = "Request",
|
|
10
|
+
Response = "Response"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export enum IPCChannel {
|
|
14
|
+
UPMServiceMessage = "UPMServiceMessage",
|
|
15
|
+
UPMServiceNewClient = "UPMServiceNewClient"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export enum NodeMessageType {
|
|
19
|
+
UPMNodeEventMessage = "UPMNodeEventMessage",
|
|
20
|
+
UPMNodeRequestResponse = "UPMNodeRequestResponseMessage",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type MessageRequestFnBase = (...args:any[]) => Promise<any>
|
|
24
|
+
export type MessageRequestMap = {}
|
|
25
|
+
export type MessageRequestNames<ReqMap extends MessageRequestMap> = keyof ReqMap
|
|
26
|
+
export type MessageRequestFn<ReqMap extends MessageRequestMap, Name extends MessageRequestNames<ReqMap>> = ReqMap[Name] extends MessageRequestFnBase ? ReqMap[Name] : never
|
|
27
|
+
export type MessageRequestParams<ReqMap extends MessageRequestMap, Name extends MessageRequestNames<ReqMap>> = ReqMap[Name] extends MessageRequestFnBase ? Parameters<ReqMap[Name]> : never
|
|
28
|
+
export type MessageRequestReturnType<ReqMap extends MessageRequestMap, Name extends MessageRequestNames<ReqMap>> = ReqMap[Name] extends MessageRequestFnBase ? Awaited<ReturnType<ReqMap[Name]>> : never
|
|
29
|
+
|
|
30
|
+
export interface Message<
|
|
31
|
+
ReqMap extends MessageRequestMap = any,
|
|
32
|
+
Type extends MessageRequestNames<ReqMap> = MessageRequestNames<ReqMap>,
|
|
33
|
+
Args extends MessageRequestParams<ReqMap, Type> = MessageRequestParams<ReqMap, Type>,
|
|
34
|
+
R extends MessageRequestReturnType<ReqMap, Type> = MessageRequestReturnType<ReqMap, Type>
|
|
35
|
+
>
|
|
36
|
+
{
|
|
37
|
+
type: Type;
|
|
38
|
+
kind: MessageKind;
|
|
39
|
+
messageId: number;
|
|
40
|
+
eventData?: any
|
|
41
|
+
args?: Args
|
|
42
|
+
result?: R
|
|
43
|
+
error?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface NodeEnvelope<Payload extends {} = any> {
|
|
47
|
+
channel: IPCChannel
|
|
48
|
+
payload: Payload
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type RequestHandler<
|
|
52
|
+
ReqMap extends MessageRequestMap = any,
|
|
53
|
+
Type extends MessageRequestNames<ReqMap> = MessageRequestNames<ReqMap>,
|
|
54
|
+
Args extends MessageRequestParams<ReqMap, Type> = MessageRequestParams<ReqMap, Type>,
|
|
55
|
+
R extends MessageRequestReturnType<ReqMap, Type> = MessageRequestReturnType<ReqMap, Type>
|
|
56
|
+
> = (type: Type, messageId: number, ...args: Args) => Promise<R>
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
export type NodeMessage<
|
|
60
|
+
ReqMap extends MessageRequestMap = any,
|
|
61
|
+
Type extends MessageRequestNames<ReqMap> = MessageRequestNames<ReqMap>> = NodeEnvelope<Message<ReqMap, Type>>
|
|
62
|
+
|
|
63
|
+
export interface PendingRequestMessage<
|
|
64
|
+
ReqMap extends MessageRequestMap = any,
|
|
65
|
+
Type extends MessageRequestNames<ReqMap> = MessageRequestNames<ReqMap>,
|
|
66
|
+
R extends MessageRequestReturnType<ReqMap, Type> = MessageRequestReturnType<ReqMap, Type>
|
|
67
|
+
>
|
|
68
|
+
{
|
|
69
|
+
deferred: Deferred<R>
|
|
70
|
+
|
|
71
|
+
timeoutId: ReturnType<typeof setTimeout>
|
|
72
|
+
|
|
73
|
+
messageId: number
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @returns true if payload was handled, false if not
|
|
78
|
+
*/
|
|
79
|
+
export type EventHandler = (clientId: string, port: Port, payload: any) => boolean
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Port of any kind
|
|
83
|
+
*/
|
|
84
|
+
export type Port = MessagePort | Electron.MessagePortMain | Electron.ParentPort | UtilityProcess
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Debugging & Inspection config
|
|
88
|
+
*/
|
|
89
|
+
export type ServiceInspectConfig = number | {
|
|
90
|
+
port: number
|
|
91
|
+
break?: boolean
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Configuration to use when creating/launching
|
|
96
|
+
* a service (`Electron.UtilityProcess`)
|
|
97
|
+
*/
|
|
98
|
+
export interface CreateServiceConfig {
|
|
99
|
+
serviceName: string
|
|
100
|
+
inspect: false | ServiceInspectConfig
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Optional/Partial `CreateServiceConfig`
|
|
105
|
+
*/
|
|
106
|
+
export type CreateServiceOptions = Partial<CreateServiceConfig>
|