electron-effect-rpc 0.1.1 → 0.3.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/README.md +81 -166
- package/dist/contract.d.ts +4 -3
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +7 -4
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/kit.d.ts +71 -0
- package/dist/kit.d.ts.map +1 -0
- package/dist/kit.js +138 -0
- package/dist/main.d.ts +4 -4
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +320 -47
- package/dist/preload.d.ts +15 -5
- package/dist/preload.d.ts.map +1 -1
- package/dist/preload.js +49 -10
- package/dist/protocol.d.ts +23 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +77 -0
- package/dist/renderer.d.ts +2 -2
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +169 -38
- package/dist/types.d.ts +90 -17
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -1
package/dist/main.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import * as S from "@effect/schema/Schema";
|
|
2
|
-
import {
|
|
2
|
+
import { Cause, Effect, Exit } from "effect";
|
|
3
3
|
import * as Runtime from "effect/Runtime";
|
|
4
|
-
import {
|
|
4
|
+
import { isNoErrorSchema } from "./contract.js";
|
|
5
|
+
import { extractErrorTag, safelyCall, toDefectEnvelope, } from "./protocol.js";
|
|
5
6
|
import { defaultChannelPrefix, } from "./types.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
function resolveChannelPrefix(prefix) {
|
|
8
|
+
return prefix ?? defaultChannelPrefix;
|
|
9
|
+
}
|
|
10
|
+
function isImplementation(value) {
|
|
11
|
+
return typeof value === "function";
|
|
12
|
+
}
|
|
13
|
+
export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
14
|
+
const channelPrefix = resolveChannelPrefix(options.channelPrefix);
|
|
15
|
+
const diagnostics = options.diagnostics;
|
|
16
|
+
const runPromiseExit = Runtime.runPromiseExit(options.runtime);
|
|
16
17
|
const implementationsByName = implementations;
|
|
17
18
|
const methodNames = new Set(contract.methods.map((method) => method.name));
|
|
18
19
|
for (const name in implementations) {
|
|
@@ -20,53 +21,325 @@ export const createRpcServer = (contract, ipc, implementations, options) => {
|
|
|
20
21
|
throw new Error(`Implementation provided for unknown RPC method: ${name}`);
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
+
function reportProtocolError(method, response, cause) {
|
|
25
|
+
safelyCall(diagnostics?.onProtocolError, {
|
|
26
|
+
method,
|
|
27
|
+
response,
|
|
28
|
+
cause,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const listeners = new Map();
|
|
32
|
+
for (const method of contract.methods) {
|
|
24
33
|
const impl = implementationsByName[method.name];
|
|
25
34
|
if (!isImplementation(impl)) {
|
|
26
35
|
throw new Error(`Missing implementation for RPC method: ${method.name}`);
|
|
27
36
|
}
|
|
28
|
-
const exitSchema = exitSchemaFor(method);
|
|
29
|
-
const encodeExit = S.encodeUnknownSync(exitSchema);
|
|
30
37
|
const decodeInput = S.decodeUnknownSync(method.req);
|
|
38
|
+
const encodeSuccess = S.encodeSync(method.res);
|
|
39
|
+
const encodeFailure = isNoErrorSchema(method.err)
|
|
40
|
+
? null
|
|
41
|
+
: S.encodeSync(method.err);
|
|
31
42
|
const channel = `${channelPrefix.rpc}${method.name}`;
|
|
32
|
-
|
|
43
|
+
listeners.set(channel, async function handleRpcRequest(_event, rawPayload) {
|
|
33
44
|
let input;
|
|
34
45
|
try {
|
|
35
46
|
input = decodeInput(rawPayload);
|
|
36
47
|
}
|
|
37
48
|
catch (cause) {
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
safelyCall(diagnostics?.onDecodeFailure, {
|
|
50
|
+
scope: "rpc-request",
|
|
51
|
+
name: method.name,
|
|
52
|
+
payload: rawPayload,
|
|
53
|
+
cause,
|
|
54
|
+
});
|
|
55
|
+
return toDefectEnvelope(cause, `RPC ${method.name} request decode failed`);
|
|
56
|
+
}
|
|
57
|
+
let effect;
|
|
58
|
+
try {
|
|
59
|
+
effect = impl(input);
|
|
60
|
+
}
|
|
61
|
+
catch (cause) {
|
|
62
|
+
return toDefectEnvelope(cause, `RPC ${method.name} implementation threw`);
|
|
63
|
+
}
|
|
64
|
+
const exit = await runPromiseExit(effect);
|
|
65
|
+
if (Exit.isSuccess(exit)) {
|
|
66
|
+
try {
|
|
67
|
+
return {
|
|
68
|
+
type: "success",
|
|
69
|
+
data: encodeSuccess(exit.value),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch (cause) {
|
|
73
|
+
reportProtocolError(method.name, exit.value, cause);
|
|
74
|
+
return toDefectEnvelope(cause, `RPC ${method.name} success encoding failed`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const failure = Cause.failureOption(exit.cause);
|
|
78
|
+
if (failure._tag === "Some") {
|
|
79
|
+
if (!encodeFailure) {
|
|
80
|
+
return toDefectEnvelope(failure.value, `RPC ${method.name} returned a typed failure, but method declares NoError`);
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
return {
|
|
84
|
+
type: "failure",
|
|
85
|
+
error: {
|
|
86
|
+
tag: extractErrorTag(failure.value),
|
|
87
|
+
data: encodeFailure(failure.value),
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (cause) {
|
|
92
|
+
reportProtocolError(method.name, failure.value, cause);
|
|
93
|
+
return toDefectEnvelope(cause, `RPC ${method.name} failure encoding failed`);
|
|
94
|
+
}
|
|
40
95
|
}
|
|
41
|
-
const
|
|
42
|
-
|
|
96
|
+
const defect = Cause.dieOption(exit.cause);
|
|
97
|
+
if (defect._tag === "Some") {
|
|
98
|
+
return toDefectEnvelope(defect.value, `RPC ${method.name} defect`);
|
|
99
|
+
}
|
|
100
|
+
return toDefectEnvelope(exit.cause, `RPC ${method.name} interrupted`);
|
|
43
101
|
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
102
|
+
}
|
|
103
|
+
let running = false;
|
|
104
|
+
let disposed = false;
|
|
105
|
+
function start() {
|
|
106
|
+
if (disposed) {
|
|
107
|
+
throw new Error("RPC endpoint has already been disposed.");
|
|
108
|
+
}
|
|
109
|
+
if (running) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const registeredChannels = [];
|
|
113
|
+
try {
|
|
114
|
+
for (const [channel, listener] of listeners) {
|
|
115
|
+
ipc.handle(channel, listener);
|
|
116
|
+
registeredChannels.push(channel);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (cause) {
|
|
120
|
+
for (const channel of registeredChannels) {
|
|
121
|
+
try {
|
|
122
|
+
ipc.removeHandler(channel);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Best-effort rollback: avoid leaving partial registration behind.
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
throw cause;
|
|
129
|
+
}
|
|
130
|
+
running = true;
|
|
131
|
+
}
|
|
132
|
+
function stop() {
|
|
133
|
+
if (!running) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
let firstError;
|
|
137
|
+
for (const channel of listeners.keys()) {
|
|
138
|
+
try {
|
|
139
|
+
ipc.removeHandler(channel);
|
|
140
|
+
}
|
|
141
|
+
catch (cause) {
|
|
142
|
+
firstError ??= cause;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
running = false;
|
|
146
|
+
if (firstError !== undefined) {
|
|
147
|
+
throw firstError;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function dispose() {
|
|
151
|
+
if (disposed) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
let stopError;
|
|
155
|
+
try {
|
|
156
|
+
stop();
|
|
157
|
+
}
|
|
158
|
+
catch (cause) {
|
|
159
|
+
stopError = cause;
|
|
160
|
+
}
|
|
161
|
+
disposed = true;
|
|
162
|
+
if (stopError !== undefined) {
|
|
163
|
+
throw stopError;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function isRunning() {
|
|
167
|
+
return running;
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
start,
|
|
171
|
+
stop,
|
|
172
|
+
dispose,
|
|
173
|
+
isRunning,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function clampQueueSize(maxQueueSize) {
|
|
177
|
+
if (maxQueueSize === undefined) {
|
|
178
|
+
return 1000;
|
|
179
|
+
}
|
|
180
|
+
if (!Number.isFinite(maxQueueSize) || maxQueueSize < 1) {
|
|
181
|
+
throw new Error("Event publisher maxQueueSize must be a positive finite number.");
|
|
182
|
+
}
|
|
183
|
+
return Math.floor(maxQueueSize);
|
|
184
|
+
}
|
|
185
|
+
export function createEventPublisher(_contract, options) {
|
|
186
|
+
const channelPrefix = resolveChannelPrefix(options.channelPrefix);
|
|
187
|
+
const diagnostics = options.diagnostics;
|
|
188
|
+
const maxQueueSize = clampQueueSize(options.maxQueueSize);
|
|
189
|
+
const queue = [];
|
|
190
|
+
let dropped = 0;
|
|
191
|
+
let running = false;
|
|
192
|
+
let disposed = false;
|
|
193
|
+
let draining = false;
|
|
194
|
+
let drainScheduled = false;
|
|
195
|
+
function scheduleDrain() {
|
|
196
|
+
if (!running || disposed || draining || drainScheduled) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
drainScheduled = true;
|
|
200
|
+
queueMicrotask(() => {
|
|
201
|
+
drainScheduled = false;
|
|
202
|
+
drain();
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
function dispatch(item) {
|
|
206
|
+
let encoded;
|
|
207
|
+
try {
|
|
208
|
+
encoded = S.encodeSync(item.event.payload)(item.payload);
|
|
209
|
+
}
|
|
210
|
+
catch (cause) {
|
|
211
|
+
dropped += 1;
|
|
212
|
+
safelyCall(diagnostics?.onDecodeFailure, {
|
|
213
|
+
scope: "event-payload",
|
|
214
|
+
name: item.event.name,
|
|
215
|
+
payload: item.payload,
|
|
216
|
+
cause,
|
|
217
|
+
});
|
|
218
|
+
safelyCall(diagnostics?.onDroppedEvent, {
|
|
219
|
+
event: item.event.name,
|
|
220
|
+
payload: item.payload,
|
|
221
|
+
reason: "encoding_failed",
|
|
222
|
+
queued: queue.length,
|
|
223
|
+
dropped,
|
|
224
|
+
});
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const window = options.getWindow();
|
|
228
|
+
if (!window || window.isDestroyed()) {
|
|
229
|
+
dropped += 1;
|
|
230
|
+
safelyCall(diagnostics?.onDroppedEvent, {
|
|
231
|
+
event: item.event.name,
|
|
232
|
+
payload: item.payload,
|
|
233
|
+
reason: "window_unavailable",
|
|
234
|
+
queued: queue.length,
|
|
235
|
+
dropped,
|
|
236
|
+
});
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
window.webContents.send(`${channelPrefix.event}${item.event.name}`, encoded);
|
|
241
|
+
}
|
|
242
|
+
catch (cause) {
|
|
243
|
+
dropped += 1;
|
|
244
|
+
safelyCall(diagnostics?.onDispatchFailure, {
|
|
245
|
+
event: item.event.name,
|
|
246
|
+
payload: item.payload,
|
|
247
|
+
cause,
|
|
248
|
+
});
|
|
249
|
+
safelyCall(diagnostics?.onDroppedEvent, {
|
|
250
|
+
event: item.event.name,
|
|
251
|
+
payload: item.payload,
|
|
252
|
+
reason: "dispatch_failed",
|
|
253
|
+
queued: queue.length,
|
|
254
|
+
dropped,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function drain() {
|
|
259
|
+
if (!running || disposed || draining) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
draining = true;
|
|
263
|
+
try {
|
|
264
|
+
while (running && !disposed && queue.length > 0) {
|
|
265
|
+
const next = queue.shift();
|
|
266
|
+
if (!next) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
dispatch(next);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
draining = false;
|
|
274
|
+
if (running && !disposed && queue.length > 0) {
|
|
275
|
+
scheduleDrain();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function enqueue(item) {
|
|
280
|
+
if (queue.length >= maxQueueSize) {
|
|
281
|
+
const evicted = queue.shift();
|
|
282
|
+
dropped += 1;
|
|
283
|
+
if (evicted) {
|
|
284
|
+
safelyCall(diagnostics?.onDroppedEvent, {
|
|
285
|
+
event: evicted.event.name,
|
|
286
|
+
payload: evicted.payload,
|
|
287
|
+
reason: "queue_full",
|
|
288
|
+
queued: queue.length,
|
|
289
|
+
dropped,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
queue.push(item);
|
|
294
|
+
scheduleDrain();
|
|
295
|
+
}
|
|
296
|
+
function publish(event, payload) {
|
|
297
|
+
return Effect.sync(() => {
|
|
298
|
+
if (disposed) {
|
|
65
299
|
return;
|
|
66
300
|
}
|
|
67
|
-
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
301
|
+
enqueue({ event, payload });
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
function start() {
|
|
305
|
+
if (disposed) {
|
|
306
|
+
throw new Error("Event publisher has already been disposed.");
|
|
307
|
+
}
|
|
308
|
+
if (running) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
running = true;
|
|
312
|
+
scheduleDrain();
|
|
313
|
+
}
|
|
314
|
+
function stop() {
|
|
315
|
+
if (!running) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
running = false;
|
|
319
|
+
}
|
|
320
|
+
function dispose() {
|
|
321
|
+
if (disposed) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
stop();
|
|
325
|
+
queue.length = 0;
|
|
326
|
+
disposed = true;
|
|
327
|
+
}
|
|
328
|
+
function isRunning() {
|
|
329
|
+
return running;
|
|
330
|
+
}
|
|
331
|
+
function stats() {
|
|
332
|
+
return {
|
|
333
|
+
queued: queue.length,
|
|
334
|
+
dropped,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
publish,
|
|
339
|
+
start,
|
|
340
|
+
stop,
|
|
341
|
+
dispose,
|
|
342
|
+
isRunning,
|
|
343
|
+
stats,
|
|
344
|
+
};
|
|
345
|
+
}
|
package/dist/preload.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
import { type ChannelPrefix } from "./types.ts";
|
|
2
|
-
type
|
|
1
|
+
import { type ChannelPrefix, type EventSubscribe, type RpcInvoke } from "./types.ts";
|
|
2
|
+
export type BridgeAdapters = {
|
|
3
|
+
readonly invoke: RpcInvoke;
|
|
4
|
+
readonly subscribe: EventSubscribe;
|
|
5
|
+
};
|
|
6
|
+
export type BridgeAdaptersOptions = {
|
|
7
|
+
readonly channelPrefix?: ChannelPrefix;
|
|
8
|
+
};
|
|
9
|
+
export type BridgeExposureOptions = BridgeAdaptersOptions & {
|
|
3
10
|
readonly rpcGlobal?: string;
|
|
4
11
|
readonly eventsGlobal?: string;
|
|
5
|
-
readonly channelPrefix?: ChannelPrefix;
|
|
6
12
|
};
|
|
7
|
-
export
|
|
8
|
-
|
|
13
|
+
export type IpcBridgeExposureOptions = BridgeAdaptersOptions & {
|
|
14
|
+
readonly global?: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function createBridgeAdapters(options?: BridgeAdaptersOptions): BridgeAdapters;
|
|
17
|
+
export declare function exposeRpcBridge(options?: BridgeExposureOptions): void;
|
|
18
|
+
export declare function exposeIpcBridge(options?: IpcBridgeExposureOptions): void;
|
|
9
19
|
//# sourceMappingURL=preload.d.ts.map
|
package/dist/preload.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preload.d.ts","sourceRoot":"","sources":["../src/preload.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"preload.d.ts","sourceRoot":"","sources":["../src/preload.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,qBAAqB,GAAG;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,qBAAqB,GAAG;IAC7D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAwCF,wBAAgB,oBAAoB,CAClC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAuBhB;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAgBrE;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAYxE"}
|
package/dist/preload.js
CHANGED
|
@@ -1,17 +1,56 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { defaultChannelPrefix } from "./types.js";
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
1
|
+
import * as electronModule from "electron";
|
|
2
|
+
import { defaultChannelPrefix, } from "./types.js";
|
|
3
|
+
function resolveElectronRendererBindings() {
|
|
4
|
+
const moduleDefault = electronModule.default;
|
|
5
|
+
const source = moduleDefault && typeof moduleDefault === "object"
|
|
6
|
+
? moduleDefault
|
|
7
|
+
: electronModule;
|
|
8
|
+
const contextBridge = source.contextBridge;
|
|
9
|
+
const ipcRenderer = source.ipcRenderer;
|
|
10
|
+
if (!contextBridge || !ipcRenderer) {
|
|
11
|
+
throw new Error("electron-effect-rpc/preload requires Electron preload runtime bindings.");
|
|
12
|
+
}
|
|
13
|
+
return { contextBridge, ipcRenderer };
|
|
14
|
+
}
|
|
15
|
+
export function createBridgeAdapters(options) {
|
|
6
16
|
const channelPrefix = options?.channelPrefix ?? defaultChannelPrefix;
|
|
17
|
+
const { ipcRenderer } = resolveElectronRendererBindings();
|
|
7
18
|
const invoke = (method, payload) => ipcRenderer.invoke(`${channelPrefix.rpc}${method}`, payload);
|
|
8
19
|
const subscribe = (event, listener) => {
|
|
9
20
|
const wrapped = (_event, payload) => listener(payload);
|
|
10
|
-
|
|
21
|
+
const channel = `${channelPrefix.event}${event}`;
|
|
22
|
+
ipcRenderer.on(channel, wrapped);
|
|
11
23
|
return () => {
|
|
12
|
-
ipcRenderer.removeListener(
|
|
24
|
+
ipcRenderer.removeListener(channel, wrapped);
|
|
13
25
|
};
|
|
14
26
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
return {
|
|
28
|
+
invoke,
|
|
29
|
+
subscribe,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function exposeRpcBridge(options) {
|
|
33
|
+
const rpcGlobal = options?.rpcGlobal ?? "rpc";
|
|
34
|
+
const eventsGlobal = options?.eventsGlobal ?? "events";
|
|
35
|
+
const { contextBridge } = resolveElectronRendererBindings();
|
|
36
|
+
const adapters = createBridgeAdapters({
|
|
37
|
+
channelPrefix: options?.channelPrefix,
|
|
38
|
+
});
|
|
39
|
+
contextBridge.exposeInMainWorld(rpcGlobal, {
|
|
40
|
+
invoke: adapters.invoke,
|
|
41
|
+
});
|
|
42
|
+
contextBridge.exposeInMainWorld(eventsGlobal, {
|
|
43
|
+
subscribe: adapters.subscribe,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export function exposeIpcBridge(options) {
|
|
47
|
+
const global = options?.global ?? "api";
|
|
48
|
+
const { contextBridge } = resolveElectronRendererBindings();
|
|
49
|
+
const adapters = createBridgeAdapters({
|
|
50
|
+
channelPrefix: options?.channelPrefix,
|
|
51
|
+
});
|
|
52
|
+
contextBridge.exposeInMainWorld(global, {
|
|
53
|
+
invoke: adapters.invoke,
|
|
54
|
+
subscribe: adapters.subscribe,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type RpcSuccessEnvelope = {
|
|
2
|
+
readonly type: "success";
|
|
3
|
+
readonly data: unknown;
|
|
4
|
+
};
|
|
5
|
+
export type RpcFailureEnvelope = {
|
|
6
|
+
readonly type: "failure";
|
|
7
|
+
readonly error: {
|
|
8
|
+
readonly tag: string;
|
|
9
|
+
readonly data: unknown;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export type RpcDefectEnvelope = {
|
|
13
|
+
readonly type: "defect";
|
|
14
|
+
readonly message: string;
|
|
15
|
+
readonly cause?: unknown;
|
|
16
|
+
};
|
|
17
|
+
export type RpcResponseEnvelope = RpcSuccessEnvelope | RpcFailureEnvelope | RpcDefectEnvelope;
|
|
18
|
+
export declare function formatUnknown(value: unknown): string;
|
|
19
|
+
export declare function extractErrorTag(error: unknown): string;
|
|
20
|
+
export declare function toDefectEnvelope(cause: unknown, prefix?: string): RpcDefectEnvelope;
|
|
21
|
+
export declare function safelyCall<T>(callback: ((context: T) => void) | undefined, context: T): void;
|
|
22
|
+
export declare function parseRpcResponseEnvelope(value: unknown): RpcResponseEnvelope | null;
|
|
23
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;KACxB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC3B,kBAAkB,GAClB,kBAAkB,GAClB,iBAAiB,CAAC;AAUtB,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAUtD;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,MAAM,GACd,iBAAiB,CAOnB;AAED,wBAAgB,UAAU,CAAC,CAAC,EAC1B,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,EAC5C,OAAO,EAAE,CAAC,GACT,IAAI,CAUN;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,GACb,mBAAmB,GAAG,IAAI,CA+C5B"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
function hasOwn(record, key) {
|
|
2
|
+
return Object.prototype.hasOwnProperty.call(record, key);
|
|
3
|
+
}
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === "object" && value !== null;
|
|
6
|
+
}
|
|
7
|
+
export function formatUnknown(value) {
|
|
8
|
+
return value instanceof Error ? value.message : String(value);
|
|
9
|
+
}
|
|
10
|
+
export function extractErrorTag(error) {
|
|
11
|
+
if (isRecord(error) && typeof error._tag === "string") {
|
|
12
|
+
return error._tag;
|
|
13
|
+
}
|
|
14
|
+
if (error instanceof Error && error.name.length > 0) {
|
|
15
|
+
return error.name;
|
|
16
|
+
}
|
|
17
|
+
return "RpcError";
|
|
18
|
+
}
|
|
19
|
+
export function toDefectEnvelope(cause, prefix) {
|
|
20
|
+
const causeText = formatUnknown(cause);
|
|
21
|
+
return {
|
|
22
|
+
type: "defect",
|
|
23
|
+
message: prefix ? `${prefix}: ${causeText}` : causeText,
|
|
24
|
+
cause: causeText,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function safelyCall(callback, context) {
|
|
28
|
+
if (!callback) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
callback(context);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Diagnostics hooks must never crash transport internals.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function parseRpcResponseEnvelope(value) {
|
|
39
|
+
if (!isRecord(value) || typeof value.type !== "string") {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
switch (value.type) {
|
|
43
|
+
case "success":
|
|
44
|
+
if (!hasOwn(value, "data")) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
type: "success",
|
|
49
|
+
data: value.data,
|
|
50
|
+
};
|
|
51
|
+
case "failure":
|
|
52
|
+
if (!isRecord(value.error) || typeof value.error.tag !== "string") {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (!hasOwn(value.error, "data")) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
type: "failure",
|
|
60
|
+
error: {
|
|
61
|
+
tag: value.error.tag,
|
|
62
|
+
data: value.error.data,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
case "defect":
|
|
66
|
+
if (typeof value.message !== "string") {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
type: "defect",
|
|
71
|
+
message: value.message,
|
|
72
|
+
cause: value.cause,
|
|
73
|
+
};
|
|
74
|
+
default:
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
package/dist/renderer.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type RpcContract } from "./contract.ts";
|
|
2
2
|
import { type AnyEvent, type AnyMethod, type EventSubscriber, type EventSubscriberOptions, type RpcClient, type RpcClientOptions } from "./types.ts";
|
|
3
|
-
export declare
|
|
4
|
-
export declare
|
|
3
|
+
export declare function createRpcClient<const Methods extends ReadonlyArray<AnyMethod>, const Events extends ReadonlyArray<AnyEvent>>(contract: RpcContract<Methods, Events>, options: RpcClientOptions): RpcClient<RpcContract<Methods, Events>>;
|
|
4
|
+
export declare function createEventSubscriber<const Methods extends ReadonlyArray<AnyMethod>, const Events extends ReadonlyArray<AnyEvent>>(contract: RpcContract<Methods, Events>, options: EventSubscriberOptions): EventSubscriber<RpcContract<Methods, Events>>;
|
|
5
5
|
export { RpcDefectError } from "./types.ts";
|
|
6
6
|
//# sourceMappingURL=renderer.d.ts.map
|
package/dist/renderer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,WAAW,EAIjB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,SAAS,EAGd,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAE3B,KAAK,SAAS,EACd,KAAK,gBAAgB,EAEtB,MAAM,YAAY,CAAC;AAuDpB,wBAAgB,eAAe,CAC7B,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAE5C,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,EACtC,OAAO,EAAE,gBAAgB,GACxB,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAwIzC;AAqBD,wBAAgB,qBAAqB,CACnC,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAE5C,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,EACtC,OAAO,EAAE,sBAAsB,GAC9B,eAAe,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAyF/C;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|