electron-effect-rpc 0.9.0 → 0.10.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 +20 -12
- 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 +59 -53
- 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 +172 -120
- 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/renderer.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as S from "effect/Schema";
|
|
2
|
-
import { Cause, Effect, Exit, Stream } from "effect";
|
|
2
|
+
import { Cause, Effect, Exit, Queue, Result, Stream } from "effect";
|
|
3
|
+
import { isRecord, toDiagnosticCause, } from "./boundary.js";
|
|
3
4
|
import { exitSchemaFor, isNoErrorSchema, } from "./contract.js";
|
|
4
|
-
import { extractStreamIdFromRaw, formatUnknown,
|
|
5
|
+
import { extractStreamIdFromRaw, formatUnknown, parseRpcResponseEnvelope, parseStreamFrame, safelyCall, } from "./protocol.js";
|
|
5
6
|
import { RpcDefectError, } from "./types.js";
|
|
6
7
|
function requireInvoke(options) {
|
|
7
8
|
if (!options?.invoke) {
|
|
@@ -15,6 +16,9 @@ function requireSubscribe(options) {
|
|
|
15
16
|
}
|
|
16
17
|
return options.subscribe;
|
|
17
18
|
}
|
|
19
|
+
function emptyRpcClient() {
|
|
20
|
+
return Object.create(null);
|
|
21
|
+
}
|
|
18
22
|
function rpcDefect(code, message, cause) {
|
|
19
23
|
return new RpcDefectError(code, message, cause);
|
|
20
24
|
}
|
|
@@ -32,13 +36,13 @@ function decodeLegacyExit(method, raw) {
|
|
|
32
36
|
if (Exit.isSuccess(exit)) {
|
|
33
37
|
return Effect.succeed(exit.value);
|
|
34
38
|
}
|
|
35
|
-
const failureOption = Cause.
|
|
39
|
+
const failureOption = Cause.findErrorOption(exit.cause);
|
|
36
40
|
if (failureOption._tag === "Some") {
|
|
37
41
|
return Effect.fail(failureOption.value);
|
|
38
42
|
}
|
|
39
|
-
const
|
|
40
|
-
if (
|
|
41
|
-
const defect =
|
|
43
|
+
const defectResult = Cause.findDefect(exit.cause);
|
|
44
|
+
if (Result.isSuccess(defectResult)) {
|
|
45
|
+
const defect = defectResult.success;
|
|
42
46
|
const message = defect instanceof Error ? defect.message : String(defect);
|
|
43
47
|
return Effect.fail(rpcDefect("remote_defect", message, defect));
|
|
44
48
|
}
|
|
@@ -59,7 +63,7 @@ export function createRpcClient(contract, options) {
|
|
|
59
63
|
scope: "rpc-response",
|
|
60
64
|
name: method.name,
|
|
61
65
|
payload: envelope.data,
|
|
62
|
-
cause,
|
|
66
|
+
cause: toDiagnosticCause(cause),
|
|
63
67
|
});
|
|
64
68
|
return rpcDefect("success_payload_decoding_failed", `RPC ${method.name} success payload decoding failed: ${formatUnknown(cause)}`, cause);
|
|
65
69
|
},
|
|
@@ -75,7 +79,7 @@ export function createRpcClient(contract, options) {
|
|
|
75
79
|
scope: "rpc-response",
|
|
76
80
|
name: method.name,
|
|
77
81
|
payload: envelope.error,
|
|
78
|
-
cause,
|
|
82
|
+
cause: toDiagnosticCause(cause),
|
|
79
83
|
});
|
|
80
84
|
return rpcDefect("failure_payload_decoding_failed", `RPC ${method.name} failure payload decoding failed: ${formatUnknown(cause)}`, cause);
|
|
81
85
|
},
|
|
@@ -90,8 +94,8 @@ export function createRpcClient(contract, options) {
|
|
|
90
94
|
safelyCall(diagnostics?.onDecodeFailure, {
|
|
91
95
|
scope: "rpc-request",
|
|
92
96
|
name: method.name,
|
|
93
|
-
payload: input,
|
|
94
|
-
cause,
|
|
97
|
+
payload: toDiagnosticCause(input),
|
|
98
|
+
cause: toDiagnosticCause(cause),
|
|
95
99
|
});
|
|
96
100
|
return rpcDefect("request_encoding_failed", `RPC ${method.name} request encoding failed: ${formatUnknown(cause)}`, cause);
|
|
97
101
|
},
|
|
@@ -101,7 +105,7 @@ export function createRpcClient(contract, options) {
|
|
|
101
105
|
safelyCall(diagnostics?.onProtocolError, {
|
|
102
106
|
method: method.name,
|
|
103
107
|
response: undefined,
|
|
104
|
-
cause,
|
|
108
|
+
cause: toDiagnosticCause(cause),
|
|
105
109
|
});
|
|
106
110
|
return rpcDefect("invoke_failed", `RPC ${method.name} invoke failed: ${formatUnknown(cause)}`, cause);
|
|
107
111
|
},
|
|
@@ -116,7 +120,7 @@ export function createRpcClient(contract, options) {
|
|
|
116
120
|
return Effect.sync(() => safelyCall(diagnostics?.onProtocolError, {
|
|
117
121
|
method: method.name,
|
|
118
122
|
response: raw,
|
|
119
|
-
cause,
|
|
123
|
+
cause: toDiagnosticCause(cause),
|
|
120
124
|
}));
|
|
121
125
|
}
|
|
122
126
|
return Effect.void;
|
|
@@ -126,18 +130,17 @@ export function createRpcClient(contract, options) {
|
|
|
126
130
|
safelyCall(diagnostics?.onProtocolError, {
|
|
127
131
|
method: method.name,
|
|
128
132
|
response: raw,
|
|
129
|
-
cause,
|
|
133
|
+
cause: toDiagnosticCause(cause),
|
|
130
134
|
});
|
|
131
135
|
return Effect.fail(cause);
|
|
132
136
|
}));
|
|
133
|
-
const client =
|
|
134
|
-
const clientRecord = client;
|
|
137
|
+
const client = emptyRpcClient();
|
|
135
138
|
for (const method of contract.methods) {
|
|
136
139
|
const caller = (...args) => {
|
|
137
140
|
const payload = args.length === 0 ? {} : args[0];
|
|
138
141
|
return call(method, payload);
|
|
139
142
|
};
|
|
140
|
-
|
|
143
|
+
Object.assign(client, { [method.name]: caller });
|
|
141
144
|
}
|
|
142
145
|
return client;
|
|
143
146
|
}
|
|
@@ -176,15 +179,15 @@ export function createEventSubscriber(contract, options) {
|
|
|
176
179
|
decoded = decoder(payload);
|
|
177
180
|
}
|
|
178
181
|
catch (cause) {
|
|
179
|
-
reportDecodeFailure(mode, event.name, payload, cause, options);
|
|
182
|
+
reportDecodeFailure(mode, event.name, payload, toDiagnosticCause(cause), options);
|
|
180
183
|
return;
|
|
181
184
|
}
|
|
182
185
|
handler(decoded);
|
|
183
186
|
});
|
|
184
187
|
return registerUnsubscribe(unsubscribe);
|
|
185
188
|
};
|
|
186
|
-
const streamEvent = (event) => Stream.
|
|
187
|
-
|
|
189
|
+
const streamEvent = (event) => Stream.callback((queue) => Effect.acquireRelease(Effect.sync(() => subscribeEvent(event, (payload) => {
|
|
190
|
+
Queue.offerUnsafe(queue, payload);
|
|
188
191
|
})), (unsubscribe) => Effect.sync(unsubscribe)));
|
|
189
192
|
const subscribeByName = (name, handler) => {
|
|
190
193
|
const event = eventMap.get(name);
|
|
@@ -198,7 +201,7 @@ export function createEventSubscriber(contract, options) {
|
|
|
198
201
|
decoded = decoder(payload);
|
|
199
202
|
}
|
|
200
203
|
catch (cause) {
|
|
201
|
-
reportDecodeFailure(mode, name, payload, cause, options);
|
|
204
|
+
reportDecodeFailure(mode, name, payload, toDiagnosticCause(cause), options);
|
|
202
205
|
return;
|
|
203
206
|
}
|
|
204
207
|
handler(decoded);
|
|
@@ -241,6 +244,15 @@ export function createStreamRpcClient(contract, options) {
|
|
|
241
244
|
const diagnostics = options.diagnostics;
|
|
242
245
|
const streamBuffer = options.streamBuffer ?? { bufferSize: "unbounded" };
|
|
243
246
|
const frameDispatcher = new Map();
|
|
247
|
+
const recentlyClosedStreams = new Map();
|
|
248
|
+
const rememberRecentlyClosed = (streamId, report) => {
|
|
249
|
+
recentlyClosedStreams.set(streamId, report);
|
|
250
|
+
queueMicrotask(() => {
|
|
251
|
+
if (recentlyClosedStreams.get(streamId) === report) {
|
|
252
|
+
recentlyClosedStreams.delete(streamId);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
};
|
|
244
256
|
// Set up central frame listener
|
|
245
257
|
const centralCleanup = onStreamFrame((raw) => {
|
|
246
258
|
const frame = parseStreamFrame(raw);
|
|
@@ -262,8 +274,14 @@ export function createStreamRpcClient(contract, options) {
|
|
|
262
274
|
return;
|
|
263
275
|
}
|
|
264
276
|
const handler = frameDispatcher.get(frame.streamId);
|
|
265
|
-
if (!handler)
|
|
266
|
-
|
|
277
|
+
if (!handler) {
|
|
278
|
+
const report = recentlyClosedStreams.get(frame.streamId);
|
|
279
|
+
if (report) {
|
|
280
|
+
recentlyClosedStreams.delete(frame.streamId);
|
|
281
|
+
report(raw);
|
|
282
|
+
}
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
267
285
|
switch (frame.type) {
|
|
268
286
|
case "data":
|
|
269
287
|
handler.data(frame.payload);
|
|
@@ -283,113 +301,146 @@ export function createStreamRpcClient(contract, options) {
|
|
|
283
301
|
}
|
|
284
302
|
});
|
|
285
303
|
const streamMethods = contract.streamMethods ?? [];
|
|
286
|
-
const
|
|
287
|
-
|
|
304
|
+
const callbackOptions = streamBuffer.bufferSize === "unbounded"
|
|
305
|
+
? undefined
|
|
306
|
+
: { bufferSize: streamBuffer.bufferSize, strategy: streamBuffer.strategy };
|
|
307
|
+
function emptyStreamRpcClient() {
|
|
308
|
+
return Object.create(null);
|
|
309
|
+
}
|
|
310
|
+
const client = emptyStreamRpcClient();
|
|
288
311
|
for (const method of streamMethods) {
|
|
289
312
|
const decodeChunk = S.decodeUnknownSync(method.chunk);
|
|
290
313
|
const encodeInput = S.encodeSync(method.req);
|
|
291
314
|
const decodeTypedError = isNoErrorSchema(method.err) ? null : S.decodeUnknownSync(method.err);
|
|
292
315
|
const caller = (...args) => {
|
|
293
316
|
const payload = args.length === 0 ? {} : args[0];
|
|
294
|
-
return Stream.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
},
|
|
356
|
-
defect: (message, fromServer = false) => {
|
|
357
|
-
if (fromServer) {
|
|
317
|
+
return Stream.callback((queue) => {
|
|
318
|
+
let queueClosed = false;
|
|
319
|
+
let postCloseReported = false;
|
|
320
|
+
const reportPostClose = (response) => {
|
|
321
|
+
if (postCloseReported)
|
|
322
|
+
return;
|
|
323
|
+
postCloseReported = true;
|
|
324
|
+
safelyCall(diagnostics?.onProtocolError, {
|
|
325
|
+
method: method.name,
|
|
326
|
+
response,
|
|
327
|
+
cause: new Error(`Stream ${method.name} received a post-close data frame; frame ignored`),
|
|
328
|
+
});
|
|
329
|
+
};
|
|
330
|
+
const failQueue = (error) => {
|
|
331
|
+
if (queueClosed)
|
|
332
|
+
return;
|
|
333
|
+
queueClosed = true;
|
|
334
|
+
Queue.failCauseUnsafe(queue, Cause.fail(error));
|
|
335
|
+
};
|
|
336
|
+
const endQueue = () => {
|
|
337
|
+
if (queueClosed)
|
|
338
|
+
return;
|
|
339
|
+
queueClosed = true;
|
|
340
|
+
Queue.endUnsafe(queue);
|
|
341
|
+
};
|
|
342
|
+
return Effect.gen(function* () {
|
|
343
|
+
const streamId = crypto.randomUUID();
|
|
344
|
+
// Once main has sent a terminal frame (end/error/defect) there is
|
|
345
|
+
// nothing left to cancel; the finalizer skips the cancel invoke.
|
|
346
|
+
let serverTerminated = false;
|
|
347
|
+
// 1. Register in dispatch map BEFORE calling invoke
|
|
348
|
+
frameDispatcher.set(streamId, {
|
|
349
|
+
data: (rawPayload) => {
|
|
350
|
+
let decoded;
|
|
351
|
+
try {
|
|
352
|
+
decoded = decodeChunk(rawPayload);
|
|
353
|
+
}
|
|
354
|
+
catch (cause) {
|
|
355
|
+
const context = {
|
|
356
|
+
scope: "stream-chunk",
|
|
357
|
+
name: method.name,
|
|
358
|
+
payload: rawPayload,
|
|
359
|
+
cause: toDiagnosticCause(cause),
|
|
360
|
+
};
|
|
361
|
+
safelyCall(diagnostics?.onDecodeFailure, context);
|
|
362
|
+
failQueue(rpcDefect("stream_chunk_decode_failed", `Stream ${method.name} chunk decode failed: ${formatUnknown(cause)}`, toDiagnosticCause(cause)));
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const accepted = !queueClosed && Queue.offerUnsafe(queue, decoded);
|
|
366
|
+
if (!accepted && (queueClosed || queue.state._tag !== "Open")) {
|
|
367
|
+
// Stream has already finished. Remove dispatcher entry on the
|
|
368
|
+
// first post-close frame to avoid repeated diagnostics.
|
|
369
|
+
frameDispatcher.delete(streamId);
|
|
370
|
+
reportPostClose(rawPayload);
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
end: () => {
|
|
374
|
+
serverTerminated = true;
|
|
375
|
+
endQueue();
|
|
376
|
+
},
|
|
377
|
+
error: (err) => {
|
|
358
378
|
serverTerminated = true;
|
|
379
|
+
if (!decodeTypedError) {
|
|
380
|
+
failQueue(rpcDefect("stream_error_decode_failed", `Stream ${method.name} received typed error but declares NoError`, err));
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
const decoded = decodeNonEmptyError(method.err, err.data);
|
|
385
|
+
failQueue(decoded);
|
|
386
|
+
}
|
|
387
|
+
catch (cause) {
|
|
388
|
+
const errContext = {
|
|
389
|
+
scope: "stream-error",
|
|
390
|
+
name: method.name,
|
|
391
|
+
payload: err,
|
|
392
|
+
cause: toDiagnosticCause(cause),
|
|
393
|
+
};
|
|
394
|
+
safelyCall(diagnostics?.onDecodeFailure, errContext);
|
|
395
|
+
failQueue(rpcDefect("stream_error_decode_failed", `Stream ${method.name} error decode failed: ${formatUnknown(cause)}`, toDiagnosticCause(cause)));
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
defect: (message, fromServer = false) => {
|
|
399
|
+
if (fromServer) {
|
|
400
|
+
serverTerminated = true;
|
|
401
|
+
}
|
|
402
|
+
failQueue(rpcDefect("remote_defect", message, undefined));
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
// 2. Register cleanup finalizer
|
|
406
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
407
|
+
frameDispatcher.delete(streamId);
|
|
408
|
+
if (queueClosed && !postCloseReported) {
|
|
409
|
+
rememberRecentlyClosed(streamId, reportPostClose);
|
|
359
410
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
}
|
|
411
|
+
}).pipe(Effect.andThen(Effect.suspend(() => serverTerminated
|
|
412
|
+
? Effect.void
|
|
413
|
+
: Effect.tryPromise(() => invoke(`stream-cancel`, { streamId })).pipe(Effect.ignore)))));
|
|
414
|
+
// 3. Encode input
|
|
415
|
+
const encodedInput = yield* Effect.try({
|
|
416
|
+
try: () => encodeInput(payload),
|
|
417
|
+
catch: (cause) => rpcDefect("request_encoding_failed", `Stream ${method.name} request encoding failed: ${formatUnknown(cause)}`, cause),
|
|
418
|
+
});
|
|
419
|
+
// 4. Initiate the stream on main
|
|
420
|
+
const response = yield* Effect.tryPromise({
|
|
421
|
+
try: () => invoke(`stream/${method.name}`, {
|
|
422
|
+
data: encodedInput,
|
|
423
|
+
streamId,
|
|
424
|
+
}),
|
|
425
|
+
catch: (cause) => rpcDefect("stream_invoke_failed", `Stream ${method.name} invoke failed: ${formatUnknown(cause)}`, cause),
|
|
426
|
+
});
|
|
427
|
+
// 5. Validate handshake response
|
|
428
|
+
const envelope = parseRpcResponseEnvelope(response);
|
|
429
|
+
if (envelope?.type === "defect") {
|
|
430
|
+
return yield* Effect.fail(rpcDefect("remote_defect", envelope.message, envelope.cause));
|
|
431
|
+
}
|
|
432
|
+
if (!isStreamStartedResponse(response)) {
|
|
433
|
+
return yield* Effect.fail(rpcDefect("stream_handshake_invalid", `Stream ${method.name} unexpected handshake response`, response));
|
|
434
|
+
}
|
|
435
|
+
}).pipe(Effect.catchCause((cause) => Effect.sync(() => {
|
|
436
|
+
if (queueClosed)
|
|
437
|
+
return;
|
|
438
|
+
queueClosed = true;
|
|
439
|
+
Queue.failCauseUnsafe(queue, cause);
|
|
440
|
+
})));
|
|
441
|
+
}, callbackOptions);
|
|
391
442
|
};
|
|
392
|
-
|
|
443
|
+
Object.assign(client, { [method.name]: caller });
|
|
393
444
|
}
|
|
394
445
|
function dispose() {
|
|
395
446
|
// Fail all active streams so consumers don't hang
|
|
@@ -397,6 +448,7 @@ export function createStreamRpcClient(contract, options) {
|
|
|
397
448
|
handler.defect("Stream client disposed");
|
|
398
449
|
frameDispatcher.delete(streamId);
|
|
399
450
|
}
|
|
451
|
+
recentlyClosedStreams.clear();
|
|
400
452
|
centralCleanup();
|
|
401
453
|
}
|
|
402
454
|
return {
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
import type { IpcEncodedValue } from "./boundary.ts";
|
|
1
2
|
import type { RpcInvoke } from "./types.ts";
|
|
2
3
|
export type Invocation = {
|
|
3
4
|
readonly method: string;
|
|
4
|
-
readonly payload:
|
|
5
|
+
readonly payload: IpcEncodedValue;
|
|
5
6
|
};
|
|
6
7
|
export type InvokeStub = RpcInvoke & {
|
|
7
8
|
readonly invocations: Invocation[];
|
|
8
9
|
};
|
|
9
10
|
export declare const createInvokeStub: (impl: RpcInvoke) => InvokeStub;
|
|
10
|
-
export declare const createDeferred: <T>() => {
|
|
11
|
+
export declare const createDeferred: <T, E = Error>() => {
|
|
11
12
|
promise: Promise<T>;
|
|
12
13
|
resolve: (value: T | PromiseLike<T>) => void;
|
|
13
|
-
reject: (reason?:
|
|
14
|
+
reject: (reason?: E) => void;
|
|
14
15
|
};
|
|
15
16
|
//# sourceMappingURL=testing.d.ts.map
|
package/dist/testing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC;AAE5E,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS,KAAG,UAYlD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,GAAG,KAAK;;qBACpB,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI;sBAC1B,CAAC,KAAK,IAAI;CAOjC,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type * as Context from "effect/Context";
|
|
1
2
|
import type * as Effect from "effect/Effect";
|
|
2
|
-
import type * as Runtime from "effect/Runtime";
|
|
3
3
|
import type * as Stream from "effect/Stream";
|
|
4
|
+
import type { DiagnosticCause, IpcEncodedValue } from "./boundary.ts";
|
|
4
5
|
import type { AnyEvent, AnyMethod, AnyStreamMethod, ExtractMethod, ExtractStreamMethod, RpcContract, RpcError, RpcEventPayload, RpcInput, RpcOutput, StreamChunk, StreamError, StreamInput } from "./contract.ts";
|
|
5
6
|
export type { AnyEvent, AnyMethod, AnyStreamMethod, ErrorSchema, ExtractMethod, ExtractStreamMethod, RpcContract, RpcError, RpcEvent, RpcEventPayload, RpcInput, RpcMethod, RpcOutput, SchemaNoContext, StreamChunk, StreamError, StreamInput, StreamRpcMethod, } from "./contract.ts";
|
|
6
7
|
/**
|
|
@@ -20,14 +21,17 @@ export type StreamImplementations<C extends RpcContract<readonly AnyMethod[], re
|
|
|
20
21
|
export type WebContentsLike = {
|
|
21
22
|
readonly id: number;
|
|
22
23
|
readonly isDestroyed: () => boolean;
|
|
23
|
-
readonly send: (channel: string, payload:
|
|
24
|
+
readonly send: (channel: string, payload: IpcEncodedValue) => void;
|
|
24
25
|
/**
|
|
25
26
|
* Optional EventEmitter surface (present on real Electron WebContents).
|
|
26
27
|
* When available, active stream fibers are interrupted as soon as the
|
|
27
28
|
* renderer is destroyed; otherwise termination happens on the next chunk.
|
|
28
29
|
*/
|
|
29
|
-
readonly once?: (event: "destroyed", listener: () => void) =>
|
|
30
|
-
readonly removeListener?: (event: "destroyed", listener: () => void) =>
|
|
30
|
+
readonly once?: (event: "destroyed", listener: () => void) => void;
|
|
31
|
+
readonly removeListener?: (event: "destroyed", listener: () => void) => void;
|
|
32
|
+
};
|
|
33
|
+
export type IpcInvokeEvent = {
|
|
34
|
+
readonly sender?: WebContentsLike;
|
|
31
35
|
};
|
|
32
36
|
type IsEmptyObject<T> = T extends object ? keyof T extends never ? string extends T ? true : false : false : false;
|
|
33
37
|
export type RpcDefectCode = "request_encoding_failed" | "invoke_failed" | "success_payload_decoding_failed" | "failure_payload_decoding_failed" | "noerror_contract_violation" | "invalid_response_envelope" | "legacy_decode_failed" | "remote_defect" | "stream_invoke_failed" | "stream_handshake_invalid" | "stream_chunk_decode_failed" | "stream_error_decode_failed";
|
|
@@ -62,28 +66,28 @@ export type DecodeFailureScope = "rpc-request" | "rpc-response" | "event-payload
|
|
|
62
66
|
export type DecodeFailureContext = {
|
|
63
67
|
readonly scope: DecodeFailureScope;
|
|
64
68
|
readonly name: string;
|
|
65
|
-
readonly payload:
|
|
66
|
-
readonly cause:
|
|
69
|
+
readonly payload: DiagnosticCause;
|
|
70
|
+
readonly cause: DiagnosticCause;
|
|
67
71
|
};
|
|
68
72
|
export type ProtocolErrorContext = {
|
|
69
73
|
readonly method: string;
|
|
70
|
-
readonly response:
|
|
71
|
-
readonly cause:
|
|
74
|
+
readonly response: DiagnosticCause;
|
|
75
|
+
readonly cause: DiagnosticCause;
|
|
72
76
|
};
|
|
73
77
|
export type DispatchFailureContext = {
|
|
74
78
|
readonly event: string;
|
|
75
|
-
readonly payload:
|
|
76
|
-
readonly cause:
|
|
79
|
+
readonly payload: DiagnosticCause;
|
|
80
|
+
readonly cause: DiagnosticCause;
|
|
77
81
|
};
|
|
78
82
|
export type DroppedEventReason = "queue_full" | "encoding_failed" | "window_unavailable" | "dispatch_failed";
|
|
79
83
|
export type DroppedEventContext = {
|
|
80
84
|
readonly event: string;
|
|
81
|
-
readonly payload:
|
|
85
|
+
readonly payload: DiagnosticCause;
|
|
82
86
|
readonly reason: DroppedEventReason;
|
|
83
87
|
readonly queued: number;
|
|
84
88
|
readonly dropped: number;
|
|
85
89
|
};
|
|
86
|
-
export type RpcInvoke = (method: string, payload:
|
|
90
|
+
export type RpcInvoke = (method: string, payload: IpcEncodedValue) => Promise<IpcEncodedValue>;
|
|
87
91
|
export type RpcResponseDecodeMode = "envelope" | "dual";
|
|
88
92
|
export type RpcClientDiagnostics = {
|
|
89
93
|
readonly onDecodeFailure?: (context: DecodeFailureContext) => void;
|
|
@@ -98,9 +102,10 @@ export type RpcEndpointDiagnostics = {
|
|
|
98
102
|
readonly onDecodeFailure?: (context: DecodeFailureContext) => void;
|
|
99
103
|
readonly onProtocolError?: (context: ProtocolErrorContext) => void;
|
|
100
104
|
};
|
|
105
|
+
export type IpcInvokeResult = IpcEncodedValue | Promise<IpcEncodedValue>;
|
|
101
106
|
export type IpcMainLike = {
|
|
102
|
-
readonly handle: (channel: string, listener: (event:
|
|
103
|
-
readonly removeHandler: (channel: string) =>
|
|
107
|
+
readonly handle: (channel: string, listener: (event: IpcInvokeEvent, payload: IpcEncodedValue) => IpcInvokeResult) => void;
|
|
108
|
+
readonly removeHandler: (channel: string) => void;
|
|
104
109
|
};
|
|
105
110
|
export interface RpcEndpoint {
|
|
106
111
|
readonly start: () => void;
|
|
@@ -110,7 +115,7 @@ export interface RpcEndpoint {
|
|
|
110
115
|
}
|
|
111
116
|
export type RpcEndpointOptions<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[], readonly AnyStreamMethod[]> = RpcContract<readonly AnyMethod[], readonly AnyEvent[], readonly []>, R = never> = {
|
|
112
117
|
readonly channelPrefix?: ChannelPrefix;
|
|
113
|
-
readonly
|
|
118
|
+
readonly context: Context.Context<R>;
|
|
114
119
|
readonly diagnostics?: RpcEndpointDiagnostics;
|
|
115
120
|
readonly streamHandlers?: StreamImplementations<C, R>;
|
|
116
121
|
};
|
|
@@ -122,7 +127,7 @@ export type EventPublisherDiagnostics = {
|
|
|
122
127
|
export type RendererWindowLike = {
|
|
123
128
|
readonly isDestroyed: () => boolean;
|
|
124
129
|
readonly webContents: {
|
|
125
|
-
readonly send: (channel: string, payload:
|
|
130
|
+
readonly send: (channel: string, payload: IpcEncodedValue) => void;
|
|
126
131
|
};
|
|
127
132
|
};
|
|
128
133
|
export type EventPublisherOptions = {
|
|
@@ -147,13 +152,14 @@ export interface RpcEventPublisher<C extends RpcContract<readonly AnyMethod[], r
|
|
|
147
152
|
* reaches two of three windows increments it once (per failed window),
|
|
148
153
|
* as do queue evictions and encoding failures.
|
|
149
154
|
*/
|
|
150
|
-
readonly stats: () =>
|
|
151
|
-
readonly queued: number;
|
|
152
|
-
readonly dropped: number;
|
|
153
|
-
};
|
|
155
|
+
readonly stats: () => EventPublisherStats;
|
|
154
156
|
}
|
|
155
157
|
export type EventDecodeMode = "safe" | "strict";
|
|
156
|
-
export type
|
|
158
|
+
export type EventPublisherStats = {
|
|
159
|
+
readonly queued: number;
|
|
160
|
+
readonly dropped: number;
|
|
161
|
+
};
|
|
162
|
+
export type EventSubscribe = (name: string, handler: (payload: IpcEncodedValue) => void) => () => void;
|
|
157
163
|
export type EventSubscriberDiagnostics = {
|
|
158
164
|
readonly onDecodeFailure?: (context: DecodeFailureContext) => void;
|
|
159
165
|
};
|
|
@@ -164,7 +170,7 @@ export type EventSubscriberOptions = {
|
|
|
164
170
|
};
|
|
165
171
|
export interface EventSubscriber<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[], readonly AnyStreamMethod[]>> {
|
|
166
172
|
readonly subscribe: <E extends C["events"][number]>(event: E, handler: (payload: RpcEventPayload<E>) => void) => () => void;
|
|
167
|
-
readonly subscribeByName: (name: string, handler: (payload:
|
|
173
|
+
readonly subscribeByName: (name: string, handler: (payload: IpcEncodedValue) => void) => () => void;
|
|
168
174
|
/**
|
|
169
175
|
* Effect-native subscription: a Stream of decoded payloads that
|
|
170
176
|
* subscribes when run and unsubscribes when its scope closes.
|
|
@@ -173,13 +179,13 @@ export interface EventSubscriber<C extends RpcContract<readonly AnyMethod[], rea
|
|
|
173
179
|
readonly stream: <E extends C["events"][number]>(event: E) => Stream.Stream<RpcEventPayload<E>>;
|
|
174
180
|
readonly dispose: () => void;
|
|
175
181
|
}
|
|
176
|
-
export type OnStreamFrame = (listener: (frame:
|
|
182
|
+
export type OnStreamFrame = (listener: (frame: IpcEncodedValue) => void) => () => void;
|
|
177
183
|
/**
|
|
178
184
|
* Stream chunk buffering policy in the renderer.
|
|
179
185
|
* Prefer `bufferSize: "unbounded"` for lossless streams such as token deltas.
|
|
180
186
|
*
|
|
181
|
-
* Bounded buffers are lossy.
|
|
182
|
-
*
|
|
187
|
+
* Bounded buffers are lossy for chunks. Effect v4's callback queue still
|
|
188
|
+
* preserves terminal completion and failure signals.
|
|
183
189
|
*/
|
|
184
190
|
export type StreamBufferOptions = {
|
|
185
191
|
readonly bufferSize: "unbounded";
|
|
@@ -197,5 +203,6 @@ export interface StreamRpcClientHandle<C extends RpcContract<readonly AnyMethod[
|
|
|
197
203
|
readonly client: StreamRpcClient<C>;
|
|
198
204
|
readonly dispose: () => void;
|
|
199
205
|
}
|
|
206
|
+
export type { DiagnosticCause, IpcEncodedRecord, IpcEncodedValue } from "./boundary.ts";
|
|
200
207
|
export type { IpcBridge, IpcBridgeGlobal, IpcKit, IpcKitOptions, IpcMainHandle } from "./kit.ts";
|
|
201
208
|
//# sourceMappingURL=types.d.ts.map
|