electron-effect-rpc 0.9.0 → 0.11.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/CHANGELOG.md +33 -11
- package/README.md +88 -38
- package/dist/boundary.d.ts +17 -0
- package/dist/boundary.d.ts.map +1 -0
- package/dist/boundary.js +89 -0
- package/dist/contract.d.ts +3 -3
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +4 -7
- package/dist/kit.d.ts +6 -8
- package/dist/kit.d.ts.map +1 -1
- package/dist/kit.js +9 -4
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +64 -59
- package/dist/preload-bridge.d.ts +36 -8
- package/dist/preload-bridge.d.ts.map +1 -1
- package/dist/preload-bridge.js +22 -20
- package/dist/protocol.d.ts +12 -12
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +35 -53
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +185 -121
- package/dist/testing.d.ts +4 -3
- package/dist/testing.d.ts.map +1 -1
- package/dist/types.d.ts +32 -25
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -5
package/dist/main.js
CHANGED
|
@@ -1,34 +1,35 @@
|
|
|
1
1
|
import * as S from "effect/Schema";
|
|
2
|
-
import { Cause, Effect, Exit,
|
|
3
|
-
import
|
|
2
|
+
import { Cause, Effect, Exit, Fiber, Result, Stream } from "effect";
|
|
3
|
+
import { isFunctionValue, isNumberValue, isRecord, isStringValue, toDiagnosticCause, } from "./boundary.js";
|
|
4
4
|
import { isNoErrorSchema } from "./contract.js";
|
|
5
|
-
import { extractErrorTag, formatUnknown,
|
|
5
|
+
import { extractErrorTag, formatUnknown, safelyCall, toDefectEnvelope, } from "./protocol.js";
|
|
6
6
|
import { assertValidChannelPrefix, defaultChannelPrefix, } from "./types.js";
|
|
7
|
+
function readNamed(record, key) {
|
|
8
|
+
return record[key];
|
|
9
|
+
}
|
|
7
10
|
function resolveChannelPrefix(prefix) {
|
|
8
11
|
return prefix ? assertValidChannelPrefix(prefix) : defaultChannelPrefix;
|
|
9
12
|
}
|
|
10
13
|
function isWebContentsLike(value) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
return (isRecord(value) &&
|
|
15
|
+
isNumberValue(value.id) &&
|
|
16
|
+
isFunctionValue(value.isDestroyed) &&
|
|
17
|
+
isFunctionValue(value.send) &&
|
|
18
|
+
(value.once === undefined || isFunctionValue(value.once)) &&
|
|
19
|
+
(value.removeListener === undefined || isFunctionValue(value.removeListener)));
|
|
20
|
+
}
|
|
21
|
+
function parseWebContentsLike(value) {
|
|
22
|
+
return isWebContentsLike(value) ? value : null;
|
|
16
23
|
}
|
|
17
24
|
function extractSender(event) {
|
|
18
25
|
if (!isRecord(event))
|
|
19
26
|
return null;
|
|
20
|
-
return
|
|
21
|
-
}
|
|
22
|
-
function isImplementation(value) {
|
|
23
|
-
return typeof value === "function";
|
|
24
|
-
}
|
|
25
|
-
function isStreamImplementation(value) {
|
|
26
|
-
return typeof value === "function";
|
|
27
|
+
return parseWebContentsLike(event.sender);
|
|
27
28
|
}
|
|
28
29
|
export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
29
30
|
const channelPrefix = resolveChannelPrefix(options.channelPrefix);
|
|
30
31
|
const diagnostics = options.diagnostics;
|
|
31
|
-
const runPromiseExit =
|
|
32
|
+
const runPromiseExit = Effect.runPromiseExitWith(options.context);
|
|
32
33
|
const implementationsByName = implementations;
|
|
33
34
|
const methodNames = new Set(contract.methods.map((method) => method.name));
|
|
34
35
|
for (const name in implementations) {
|
|
@@ -39,14 +40,14 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
39
40
|
function reportProtocolError(method, response, cause) {
|
|
40
41
|
safelyCall(diagnostics?.onProtocolError, {
|
|
41
42
|
method,
|
|
42
|
-
response,
|
|
43
|
-
cause,
|
|
43
|
+
response: toDiagnosticCause(response),
|
|
44
|
+
cause: toDiagnosticCause(cause),
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
47
|
const listeners = new Map();
|
|
47
48
|
for (const method of contract.methods) {
|
|
48
|
-
const impl = implementationsByName
|
|
49
|
-
if (!
|
|
49
|
+
const impl = readNamed(implementationsByName, method.name);
|
|
50
|
+
if (!isFunctionValue(impl)) {
|
|
50
51
|
throw new Error(`Missing implementation for RPC method: ${method.name}`);
|
|
51
52
|
}
|
|
52
53
|
const decodeInput = S.decodeUnknownSync(method.req);
|
|
@@ -63,7 +64,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
63
64
|
scope: "rpc-request",
|
|
64
65
|
name: method.name,
|
|
65
66
|
payload: rawPayload,
|
|
66
|
-
cause,
|
|
67
|
+
cause: toDiagnosticCause(cause),
|
|
67
68
|
});
|
|
68
69
|
return toDefectEnvelope(cause, `RPC ${method.name} request decode failed`);
|
|
69
70
|
}
|
|
@@ -87,7 +88,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
87
88
|
return toDefectEnvelope(cause, `RPC ${method.name} success encoding failed`);
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
|
-
const failure = Cause.
|
|
91
|
+
const failure = Cause.findErrorOption(exit.cause);
|
|
91
92
|
if (failure._tag === "Some") {
|
|
92
93
|
if (!encodeFailure) {
|
|
93
94
|
return toDefectEnvelope(failure.value, `RPC ${method.name} returned a typed failure, but method declares NoError`);
|
|
@@ -106,9 +107,9 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
106
107
|
return toDefectEnvelope(cause, `RPC ${method.name} failure encoding failed`);
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
|
-
const defect = Cause.
|
|
110
|
-
if (defect
|
|
111
|
-
return toDefectEnvelope(defect.
|
|
110
|
+
const defect = Cause.findDefect(exit.cause);
|
|
111
|
+
if (Result.isSuccess(defect)) {
|
|
112
|
+
return toDefectEnvelope(defect.success, `RPC ${method.name} defect`);
|
|
112
113
|
}
|
|
113
114
|
return toDefectEnvelope(exit.cause, `RPC ${method.name} interrupted`);
|
|
114
115
|
});
|
|
@@ -133,10 +134,10 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
133
134
|
throw new Error(`Stream implementation provided for unknown stream method: ${name}`);
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
|
-
const runFork =
|
|
137
|
+
const runFork = Effect.runForkWith(options.context);
|
|
137
138
|
for (const method of streamMethods) {
|
|
138
|
-
const impl = streamHandlerImpls
|
|
139
|
-
if (!
|
|
139
|
+
const impl = readNamed(streamHandlerImpls, method.name);
|
|
140
|
+
if (!isFunctionValue(impl)) {
|
|
140
141
|
throw new Error(`Missing implementation for stream method: ${method.name}`);
|
|
141
142
|
}
|
|
142
143
|
const decodeInput = S.decodeUnknownSync(method.req);
|
|
@@ -149,7 +150,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
149
150
|
}
|
|
150
151
|
const streamId = rawPayload.streamId;
|
|
151
152
|
const rawData = rawPayload.data;
|
|
152
|
-
if (
|
|
153
|
+
if (!isStringValue(streamId) || streamId.length === 0) {
|
|
153
154
|
return toDefectEnvelope("Invalid streamId");
|
|
154
155
|
}
|
|
155
156
|
if (activeStreams.has(streamId)) {
|
|
@@ -167,8 +168,8 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
167
168
|
const decodeCtx = {
|
|
168
169
|
scope: "stream-request",
|
|
169
170
|
name: method.name,
|
|
170
|
-
payload: rawData,
|
|
171
|
-
cause,
|
|
171
|
+
payload: toDiagnosticCause(rawData),
|
|
172
|
+
cause: toDiagnosticCause(cause),
|
|
172
173
|
};
|
|
173
174
|
safelyCall(diagnostics?.onDecodeFailure, decodeCtx);
|
|
174
175
|
return toDefectEnvelope(cause, `Stream ${method.name} request decode failed`);
|
|
@@ -180,8 +181,8 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
180
181
|
sender.send(sfChannel, frame);
|
|
181
182
|
}
|
|
182
183
|
},
|
|
183
|
-
catch:
|
|
184
|
-
}).pipe(Effect.ignore);
|
|
184
|
+
catch: toDiagnosticCause,
|
|
185
|
+
}).pipe(Effect.tapError((cause) => Effect.sync(() => reportProtocolError(method.name, frame, cause))), Effect.ignore);
|
|
185
186
|
let handlerStream;
|
|
186
187
|
try {
|
|
187
188
|
handlerStream = impl(input, { sender });
|
|
@@ -190,7 +191,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
190
191
|
return toDefectEnvelope(cause, `Stream ${method.name} implementation threw`);
|
|
191
192
|
}
|
|
192
193
|
const buildTerminalFrame = (cause) => {
|
|
193
|
-
const failure = Cause.
|
|
194
|
+
const failure = Cause.findErrorOption(cause);
|
|
194
195
|
if (failure._tag === "Some") {
|
|
195
196
|
if (encodeFailure) {
|
|
196
197
|
try {
|
|
@@ -217,14 +218,16 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
217
218
|
message: `Stream ${method.name} returned a typed failure, but method declares NoError: ${formatUnknown(failure.value)}`,
|
|
218
219
|
};
|
|
219
220
|
}
|
|
220
|
-
if (Cause.
|
|
221
|
+
if (Cause.hasInterruptsOnly(cause)) {
|
|
221
222
|
return { type: "end", streamId };
|
|
222
223
|
}
|
|
223
|
-
const defect = Cause.
|
|
224
|
+
const defect = Cause.findDefect(cause);
|
|
224
225
|
return {
|
|
225
226
|
type: "defect",
|
|
226
227
|
streamId,
|
|
227
|
-
message:
|
|
228
|
+
message: Result.isSuccess(defect)
|
|
229
|
+
? formatUnknown(defect.success)
|
|
230
|
+
: "Stream failed unexpectedly",
|
|
228
231
|
};
|
|
229
232
|
};
|
|
230
233
|
// Reserve entry BEFORE forking
|
|
@@ -233,7 +236,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
233
236
|
senderId: sender.id,
|
|
234
237
|
};
|
|
235
238
|
const onSenderDestroyed = () => {
|
|
236
|
-
entry.fiber?.
|
|
239
|
+
entry.fiber?.interruptUnsafe();
|
|
237
240
|
};
|
|
238
241
|
const streamEffect = handlerStream.pipe(Stream.mapEffect((chunk) => Effect.suspend(() => {
|
|
239
242
|
// A destroyed renderer can never consume more chunks:
|
|
@@ -241,18 +244,17 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
241
244
|
if (sender.isDestroyed()) {
|
|
242
245
|
return Effect.interrupt;
|
|
243
246
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
})), Stream.runDrain, Effect.andThen(() => trySend({ type: "end", streamId })), Effect.catchAllCause((cause) => sender.isDestroyed() ? Effect.void : trySend(buildTerminalFrame(cause))), Effect.ensuring(Effect.sync(() => {
|
|
247
|
+
// A lost chunk invalidates the stream. Let the transport boundary
|
|
248
|
+
// send a defect frame instead of reporting successful completion.
|
|
249
|
+
return Effect.sync(() => sender.send(sfChannel, { type: "data", streamId, payload: encodeChunk(chunk) }));
|
|
250
|
+
})), Stream.runDrain, Effect.andThen(() => trySend({ type: "end", streamId })), Effect.catchCause((cause) => sender.isDestroyed() ? Effect.void : trySend(buildTerminalFrame(cause))), Effect.ensuring(Effect.sync(() => {
|
|
249
251
|
activeStreams.delete(streamId);
|
|
250
|
-
if (
|
|
252
|
+
if (sender.removeListener) {
|
|
251
253
|
sender.removeListener("destroyed", onSenderDestroyed);
|
|
252
254
|
}
|
|
253
255
|
})));
|
|
254
256
|
activeStreams.set(streamId, entry);
|
|
255
|
-
if (
|
|
257
|
+
if (sender.once) {
|
|
256
258
|
sender.once("destroyed", onSenderDestroyed);
|
|
257
259
|
}
|
|
258
260
|
let fiber;
|
|
@@ -261,7 +263,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
261
263
|
}
|
|
262
264
|
catch (cause) {
|
|
263
265
|
activeStreams.delete(streamId);
|
|
264
|
-
if (
|
|
266
|
+
if (sender.removeListener) {
|
|
265
267
|
sender.removeListener("destroyed", onSenderDestroyed);
|
|
266
268
|
}
|
|
267
269
|
return toDefectEnvelope(cause, `Stream ${method.name} failed to start`);
|
|
@@ -302,7 +304,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
302
304
|
return { cancelled: false };
|
|
303
305
|
}
|
|
304
306
|
const streamId = rawPayload.streamId;
|
|
305
|
-
if (
|
|
307
|
+
if (!isStringValue(streamId))
|
|
306
308
|
return { cancelled: false };
|
|
307
309
|
const entry = activeStreams.get(streamId);
|
|
308
310
|
if (!entry)
|
|
@@ -313,7 +315,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
313
315
|
return { cancelled: false };
|
|
314
316
|
// Interrupt the fiber
|
|
315
317
|
if (entry.fiber) {
|
|
316
|
-
entry.fiber.
|
|
318
|
+
entry.fiber.interruptUnsafe();
|
|
317
319
|
}
|
|
318
320
|
return { cancelled: true };
|
|
319
321
|
});
|
|
@@ -340,7 +342,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
340
342
|
// Interrupt all active stream fibers first
|
|
341
343
|
for (const entry of activeStreams.values()) {
|
|
342
344
|
if (entry.fiber) {
|
|
343
|
-
entry.fiber.
|
|
345
|
+
entry.fiber.interruptUnsafe();
|
|
344
346
|
}
|
|
345
347
|
}
|
|
346
348
|
activeStreams.clear();
|
|
@@ -439,12 +441,12 @@ export function createEventPublisher(_contract, options) {
|
|
|
439
441
|
safelyCall(diagnostics?.onDecodeFailure, {
|
|
440
442
|
scope: "event-payload",
|
|
441
443
|
name: item.event.name,
|
|
442
|
-
payload: item.payload,
|
|
443
|
-
cause,
|
|
444
|
+
payload: toDiagnosticCause(item.payload),
|
|
445
|
+
cause: toDiagnosticCause(cause),
|
|
444
446
|
});
|
|
445
447
|
safelyCall(diagnostics?.onDroppedEvent, {
|
|
446
448
|
event: item.event.name,
|
|
447
|
-
payload: item.payload,
|
|
449
|
+
payload: toDiagnosticCause(item.payload),
|
|
448
450
|
reason: "encoding_failed",
|
|
449
451
|
queued: queue.length,
|
|
450
452
|
dropped,
|
|
@@ -456,7 +458,7 @@ export function createEventPublisher(_contract, options) {
|
|
|
456
458
|
dropped += 1;
|
|
457
459
|
safelyCall(diagnostics?.onDroppedEvent, {
|
|
458
460
|
event: item.event.name,
|
|
459
|
-
payload: item.payload,
|
|
461
|
+
payload: toDiagnosticCause(item.payload),
|
|
460
462
|
reason: "window_unavailable",
|
|
461
463
|
queued: queue.length,
|
|
462
464
|
dropped,
|
|
@@ -469,7 +471,7 @@ export function createEventPublisher(_contract, options) {
|
|
|
469
471
|
dropped += 1;
|
|
470
472
|
safelyCall(diagnostics?.onDroppedEvent, {
|
|
471
473
|
event: item.event.name,
|
|
472
|
-
payload: item.payload,
|
|
474
|
+
payload: toDiagnosticCause(item.payload),
|
|
473
475
|
reason: "window_unavailable",
|
|
474
476
|
queued: queue.length,
|
|
475
477
|
dropped,
|
|
@@ -483,12 +485,12 @@ export function createEventPublisher(_contract, options) {
|
|
|
483
485
|
dropped += 1;
|
|
484
486
|
safelyCall(diagnostics?.onDispatchFailure, {
|
|
485
487
|
event: item.event.name,
|
|
486
|
-
payload: item.payload,
|
|
487
|
-
cause,
|
|
488
|
+
payload: toDiagnosticCause(item.payload),
|
|
489
|
+
cause: toDiagnosticCause(cause),
|
|
488
490
|
});
|
|
489
491
|
safelyCall(diagnostics?.onDroppedEvent, {
|
|
490
492
|
event: item.event.name,
|
|
491
|
-
payload: item.payload,
|
|
493
|
+
payload: toDiagnosticCause(item.payload),
|
|
492
494
|
reason: "dispatch_failed",
|
|
493
495
|
queued: queue.length,
|
|
494
496
|
dropped,
|
|
@@ -502,7 +504,10 @@ export function createEventPublisher(_contract, options) {
|
|
|
502
504
|
}
|
|
503
505
|
draining = true;
|
|
504
506
|
try {
|
|
505
|
-
while (
|
|
507
|
+
while (queue.length > 0) {
|
|
508
|
+
if (!running || disposed) {
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
506
511
|
const next = queue.shift();
|
|
507
512
|
if (!next) {
|
|
508
513
|
continue;
|
|
@@ -524,7 +529,7 @@ export function createEventPublisher(_contract, options) {
|
|
|
524
529
|
if (evicted) {
|
|
525
530
|
safelyCall(diagnostics?.onDroppedEvent, {
|
|
526
531
|
event: evicted.event.name,
|
|
527
|
-
payload: evicted.payload,
|
|
532
|
+
payload: toDiagnosticCause(evicted.payload),
|
|
528
533
|
reason: "queue_full",
|
|
529
534
|
queued: queue.length,
|
|
530
535
|
dropped,
|
package/dist/preload-bridge.d.ts
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type IpcEncodedValue } from "./boundary.ts";
|
|
2
|
+
import { type ChannelPrefix, type EventSubscribe, type IpcInvokeEvent, type OnStreamFrame, type RpcInvoke } from "./types.ts";
|
|
3
|
+
export type ExposedRpcApi = {
|
|
4
|
+
readonly invoke: RpcInvoke;
|
|
5
|
+
readonly onStreamFrame: OnStreamFrame;
|
|
6
|
+
};
|
|
7
|
+
export type ExposedEventsApi = {
|
|
8
|
+
readonly subscribe: EventSubscribe;
|
|
9
|
+
};
|
|
10
|
+
export type ExposedIpcApi = {
|
|
11
|
+
readonly invoke: RpcInvoke;
|
|
12
|
+
readonly subscribe: EventSubscribe;
|
|
13
|
+
readonly onStreamFrame: OnStreamFrame;
|
|
14
|
+
};
|
|
15
|
+
export type ExposedBridgeValue = ExposedRpcApi | ExposedEventsApi | ExposedIpcApi;
|
|
2
16
|
export type BridgeAdapters = {
|
|
3
17
|
readonly invoke: RpcInvoke;
|
|
4
18
|
readonly subscribe: EventSubscribe;
|
|
5
|
-
readonly onStreamFrame:
|
|
19
|
+
readonly onStreamFrame: OnStreamFrame;
|
|
6
20
|
};
|
|
7
21
|
export type BridgeAdaptersOptions = {
|
|
8
22
|
readonly channelPrefix?: ChannelPrefix;
|
|
@@ -15,18 +29,32 @@ export type IpcBridgeExposureOptions = BridgeAdaptersOptions & {
|
|
|
15
29
|
readonly global?: string;
|
|
16
30
|
};
|
|
17
31
|
type ContextBridgeLike = {
|
|
18
|
-
readonly exposeInMainWorld: (name: string, value:
|
|
32
|
+
readonly exposeInMainWorld: (name: string, value: ExposedBridgeValue) => void;
|
|
19
33
|
};
|
|
20
34
|
type IpcRendererLike = {
|
|
21
|
-
readonly invoke: (channel: string, payload:
|
|
22
|
-
readonly on: (channel: string, handler: (event:
|
|
23
|
-
readonly removeListener: (channel: string, handler: (event:
|
|
35
|
+
readonly invoke: (channel: string, payload: IpcEncodedValue) => Promise<IpcEncodedValue>;
|
|
36
|
+
readonly on: (channel: string, handler: (event: IpcInvokeEvent, payload: IpcEncodedValue) => void) => void;
|
|
37
|
+
readonly removeListener: (channel: string, handler: (event: IpcInvokeEvent, payload: IpcEncodedValue) => void) => void;
|
|
24
38
|
};
|
|
25
|
-
type ElectronRendererBindings = {
|
|
39
|
+
export type ElectronRendererBindings = {
|
|
26
40
|
readonly contextBridge: ContextBridgeLike;
|
|
27
41
|
readonly ipcRenderer: IpcRendererLike;
|
|
28
42
|
};
|
|
29
|
-
|
|
43
|
+
type ElectronRuntimeFunction = (...arguments_: never[]) => void;
|
|
44
|
+
type ElectronDirectModuleCandidate = {
|
|
45
|
+
readonly contextBridge: {
|
|
46
|
+
readonly exposeInMainWorld: ElectronRuntimeFunction;
|
|
47
|
+
};
|
|
48
|
+
readonly ipcRenderer: {
|
|
49
|
+
readonly invoke: ElectronRuntimeFunction;
|
|
50
|
+
readonly on: ElectronRuntimeFunction;
|
|
51
|
+
readonly removeListener: ElectronRuntimeFunction;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
export type ElectronModuleCandidate = ElectronDirectModuleCandidate | {
|
|
55
|
+
readonly default: ElectronModuleCandidate;
|
|
56
|
+
};
|
|
57
|
+
export declare function resolveElectronRendererBindings<T>(moduleCandidate: T): ElectronRendererBindings;
|
|
30
58
|
export declare function createBridgeAdaptersFromBindings(bindings: ElectronRendererBindings, options?: BridgeAdaptersOptions): BridgeAdapters;
|
|
31
59
|
export declare function exposeRpcBridgeFromBindings(bindings: ElectronRendererBindings, options?: BridgeExposureOptions): void;
|
|
32
60
|
export declare function exposeIpcBridgeFromBindings(bindings: ElectronRendererBindings, options?: IpcBridgeExposureOptions): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preload-bridge.d.ts","sourceRoot":"","sources":["../src/preload-bridge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"preload-bridge.d.ts","sourceRoot":"","sources":["../src/preload-bridge.ts"],"names":[],"mappings":"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"}
|
package/dist/preload-bridge.js
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
1
|
-
import { isRecord } from "./
|
|
1
|
+
import { isFunctionValue, isRecord } from "./boundary.js";
|
|
2
2
|
import { assertValidChannelPrefix, defaultChannelPrefix, } from "./types.js";
|
|
3
3
|
function isContextBridgeLike(value) {
|
|
4
|
-
return isRecord(value) &&
|
|
4
|
+
return isRecord(value) && isFunctionValue(value.exposeInMainWorld);
|
|
5
5
|
}
|
|
6
6
|
function isIpcRendererLike(value) {
|
|
7
7
|
return (isRecord(value) &&
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
isFunctionValue(value.invoke) &&
|
|
9
|
+
isFunctionValue(value.on) &&
|
|
10
|
+
isFunctionValue(value.removeListener));
|
|
11
|
+
}
|
|
12
|
+
function isElectronRendererBindings(value) {
|
|
13
|
+
return (isRecord(value) &&
|
|
14
|
+
isContextBridgeLike(value.contextBridge) &&
|
|
15
|
+
isIpcRendererLike(value.ipcRenderer));
|
|
16
|
+
}
|
|
17
|
+
function parseElectronRendererBindings(value) {
|
|
18
|
+
return isElectronRendererBindings(value) ? value : null;
|
|
11
19
|
}
|
|
12
20
|
export function resolveElectronRendererBindings(moduleCandidate) {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
: isRecord(moduleDefault)
|
|
17
|
-
? moduleDefault
|
|
18
|
-
: undefined;
|
|
19
|
-
if (!source) {
|
|
20
|
-
throw new Error("electron-effect-rpc/preload requires Electron preload runtime bindings.");
|
|
21
|
+
const direct = parseElectronRendererBindings(moduleCandidate);
|
|
22
|
+
if (direct) {
|
|
23
|
+
return direct;
|
|
21
24
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
if (isRecord(moduleCandidate)) {
|
|
26
|
+
const fromDefault = parseElectronRendererBindings(moduleCandidate.default);
|
|
27
|
+
if (fromDefault) {
|
|
28
|
+
return fromDefault;
|
|
29
|
+
}
|
|
25
30
|
}
|
|
26
|
-
|
|
27
|
-
contextBridge,
|
|
28
|
-
ipcRenderer,
|
|
29
|
-
};
|
|
31
|
+
throw new Error("electron-effect-rpc/preload requires Electron preload runtime bindings.");
|
|
30
32
|
}
|
|
31
33
|
export function createBridgeAdaptersFromBindings(bindings, options) {
|
|
32
34
|
const channelPrefix = options?.channelPrefix
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
+
import { type IpcEncodedValue } from "./boundary.ts";
|
|
1
2
|
export type RpcSuccessEnvelope = {
|
|
2
3
|
readonly type: "success";
|
|
3
|
-
readonly data:
|
|
4
|
+
readonly data: IpcEncodedValue;
|
|
4
5
|
};
|
|
5
6
|
export type RpcFailureEnvelope = {
|
|
6
7
|
readonly type: "failure";
|
|
7
8
|
readonly error: {
|
|
8
9
|
readonly tag: string;
|
|
9
|
-
readonly data:
|
|
10
|
+
readonly data: IpcEncodedValue;
|
|
10
11
|
};
|
|
11
12
|
};
|
|
12
13
|
export type RpcDefectEnvelope = {
|
|
13
14
|
readonly type: "defect";
|
|
14
15
|
readonly message: string;
|
|
15
|
-
readonly cause?:
|
|
16
|
+
readonly cause?: IpcEncodedValue;
|
|
16
17
|
};
|
|
17
18
|
export type RpcResponseEnvelope = RpcSuccessEnvelope | RpcFailureEnvelope | RpcDefectEnvelope;
|
|
18
|
-
export
|
|
19
|
-
export declare function
|
|
20
|
-
export declare function
|
|
21
|
-
export declare function toDefectEnvelope(cause: unknown, prefix?: string): RpcDefectEnvelope;
|
|
19
|
+
export { formatUnknown, isRecord, isStringValue } from "./boundary.ts";
|
|
20
|
+
export declare function extractErrorTag<T>(error: T): string;
|
|
21
|
+
export declare function toDefectEnvelope<T>(cause: T, prefix?: string): RpcDefectEnvelope;
|
|
22
22
|
export declare function safelyCall<T>(callback: ((context: T) => void) | undefined, context: T): void;
|
|
23
23
|
export type StreamDataFrame = {
|
|
24
24
|
readonly type: "data";
|
|
25
25
|
readonly streamId: string;
|
|
26
|
-
readonly payload:
|
|
26
|
+
readonly payload: IpcEncodedValue;
|
|
27
27
|
};
|
|
28
28
|
export type StreamEndFrame = {
|
|
29
29
|
readonly type: "end";
|
|
@@ -34,7 +34,7 @@ export type StreamErrorFrame = {
|
|
|
34
34
|
readonly streamId: string;
|
|
35
35
|
readonly error: {
|
|
36
36
|
readonly tag: string;
|
|
37
|
-
readonly data:
|
|
37
|
+
readonly data: IpcEncodedValue;
|
|
38
38
|
};
|
|
39
39
|
};
|
|
40
40
|
export type StreamDefectFrame = {
|
|
@@ -43,7 +43,7 @@ export type StreamDefectFrame = {
|
|
|
43
43
|
readonly message: string;
|
|
44
44
|
};
|
|
45
45
|
export type StreamFrame = StreamDataFrame | StreamEndFrame | StreamErrorFrame | StreamDefectFrame;
|
|
46
|
-
export declare function extractStreamIdFromRaw(value:
|
|
47
|
-
export declare function parseStreamFrame(value:
|
|
48
|
-
export declare function parseRpcResponseEnvelope(value:
|
|
46
|
+
export declare function extractStreamIdFromRaw<T>(value: T): string | null;
|
|
47
|
+
export declare function parseStreamFrame<T>(value: T): StreamFrame | null;
|
|
48
|
+
export declare function parseRpcResponseEnvelope<T>(value: T): RpcResponseEnvelope | null;
|
|
49
49
|
//# sourceMappingURL=protocol.d.ts.map
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;CAChC,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,eAAe,CAAC;KAChC,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,eAAe,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,GAAG,kBAAkB,GAAG,iBAAiB,CAAC;AAM9F,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEvE,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAUnD;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAOhF;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAU5F;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;KAChC,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,cAAc,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;AAMlG,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,GAAG,IAAI,CAMjE;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,WAAW,GAAG,IAAI,CAiChE;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,mBAAmB,GAAG,IAAI,CAyBhF"}
|
package/dist/protocol.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
export function formatUnknown(value) {
|
|
8
|
-
return value instanceof Error ? value.message : String(value);
|
|
9
|
-
}
|
|
1
|
+
import * as S from "effect/Schema";
|
|
2
|
+
import { formatUnknown, isRecord, isStringValue, } from "./boundary.js";
|
|
3
|
+
const TaggedValue = S.Struct({
|
|
4
|
+
_tag: S.String,
|
|
5
|
+
});
|
|
6
|
+
export { formatUnknown, isRecord, isStringValue } from "./boundary.js";
|
|
10
7
|
export function extractErrorTag(error) {
|
|
11
|
-
if (
|
|
8
|
+
if (S.is(TaggedValue)(error)) {
|
|
12
9
|
return error._tag;
|
|
13
10
|
}
|
|
14
11
|
if (error instanceof Error && error.name.length > 0) {
|
|
@@ -35,84 +32,69 @@ export function safelyCall(callback, context) {
|
|
|
35
32
|
// Diagnostics hooks must never crash transport internals.
|
|
36
33
|
}
|
|
37
34
|
}
|
|
35
|
+
function hasOwn(record, key) {
|
|
36
|
+
return Object.prototype.hasOwnProperty.call(record, key);
|
|
37
|
+
}
|
|
38
38
|
export function extractStreamIdFromRaw(value) {
|
|
39
|
-
if (!isRecord(value)
|
|
39
|
+
if (!isRecord(value)) {
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
const streamId = value.streamId;
|
|
43
|
+
return isStringValue(streamId) ? streamId : null;
|
|
43
44
|
}
|
|
44
45
|
export function parseStreamFrame(value) {
|
|
45
|
-
if (!isRecord(value)
|
|
46
|
+
if (!isRecord(value)) {
|
|
46
47
|
return null;
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
+
const streamId = extractStreamIdFromRaw(value);
|
|
50
|
+
if (streamId === null) {
|
|
49
51
|
return null;
|
|
50
52
|
}
|
|
51
|
-
const streamId = value.streamId;
|
|
52
53
|
switch (value.type) {
|
|
53
54
|
case "data":
|
|
54
|
-
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
return { type: "data", streamId, payload: value.payload };
|
|
55
|
+
return hasOwn(value, "payload") ? { type: "data", streamId, payload: value.payload } : null;
|
|
58
56
|
case "end":
|
|
59
57
|
return { type: "end", streamId };
|
|
60
|
-
case "error":
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
!hasOwn(value.error, "data")) {
|
|
58
|
+
case "error": {
|
|
59
|
+
const error = value.error;
|
|
60
|
+
if (!isRecord(error) || !isStringValue(error.tag) || !hasOwn(error, "data")) {
|
|
64
61
|
return null;
|
|
65
62
|
}
|
|
66
63
|
return {
|
|
67
64
|
type: "error",
|
|
68
65
|
streamId,
|
|
69
|
-
error: { tag:
|
|
66
|
+
error: { tag: error.tag, data: error.data },
|
|
70
67
|
};
|
|
68
|
+
}
|
|
71
69
|
case "defect":
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return { type: "defect", streamId, message: value.message };
|
|
70
|
+
return isStringValue(value.message)
|
|
71
|
+
? { type: "defect", streamId, message: value.message }
|
|
72
|
+
: null;
|
|
76
73
|
default:
|
|
77
74
|
return null;
|
|
78
75
|
}
|
|
79
76
|
}
|
|
80
77
|
export function parseRpcResponseEnvelope(value) {
|
|
81
|
-
if (!isRecord(value)
|
|
78
|
+
if (!isRecord(value)) {
|
|
82
79
|
return null;
|
|
83
80
|
}
|
|
84
81
|
switch (value.type) {
|
|
85
82
|
case "success":
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
type: "success",
|
|
91
|
-
data: value.data,
|
|
92
|
-
};
|
|
93
|
-
case "failure":
|
|
94
|
-
if (!isRecord(value.error) || typeof value.error.tag !== "string") {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
if (!hasOwn(value.error, "data")) {
|
|
83
|
+
return hasOwn(value, "data") ? { type: "success", data: value.data } : null;
|
|
84
|
+
case "failure": {
|
|
85
|
+
const error = value.error;
|
|
86
|
+
if (!isRecord(error) || !isStringValue(error.tag) || !hasOwn(error, "data")) {
|
|
98
87
|
return null;
|
|
99
88
|
}
|
|
100
89
|
return {
|
|
101
90
|
type: "failure",
|
|
102
|
-
error: {
|
|
103
|
-
tag: value.error.tag,
|
|
104
|
-
data: value.error.data,
|
|
105
|
-
},
|
|
91
|
+
error: { tag: error.tag, data: error.data },
|
|
106
92
|
};
|
|
93
|
+
}
|
|
107
94
|
case "defect":
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return {
|
|
112
|
-
type: "defect",
|
|
113
|
-
message: value.message,
|
|
114
|
-
cause: value.cause,
|
|
115
|
-
};
|
|
95
|
+
return isStringValue(value.message)
|
|
96
|
+
? { type: "defect", message: value.message, cause: value.cause }
|
|
97
|
+
: null;
|
|
116
98
|
default:
|
|
117
99
|
return null;
|
|
118
100
|
}
|