electron-effect-rpc 0.1.1 → 0.2.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 +47 -18
- package/dist/contract.d.ts +4 -3
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +7 -4
- package/dist/main.d.ts +4 -4
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +271 -47
- package/dist/preload.d.ts +11 -5
- package/dist/preload.d.ts.map +1 -1
- package/dist/preload.js +23 -9
- 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 +147 -39
- package/dist/types.d.ts +87 -15
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,9 +11,10 @@ uses Electron 38) and assumes ESM-capable bundling.
|
|
|
11
11
|
- Single shared contract for methods and events.
|
|
12
12
|
- End-to-end type safety using @effect/schema.
|
|
13
13
|
- Promise-based renderer client with typed errors.
|
|
14
|
-
- Effect-based main handlers with
|
|
15
|
-
-
|
|
16
|
-
-
|
|
14
|
+
- Effect-based main handlers with explicit runtime injection.
|
|
15
|
+
- Explicit lifecycle handles for main-process RPC and event publishing.
|
|
16
|
+
- Bounded event queue with drop-oldest backpressure.
|
|
17
|
+
- Structured diagnostics hooks for decode/protocol/dispatch failures.
|
|
17
18
|
|
|
18
19
|
## Requirements
|
|
19
20
|
- Electron with context isolation enabled.
|
|
@@ -99,36 +100,43 @@ export const ReadTextFile = rpc(
|
|
|
99
100
|
```ts
|
|
100
101
|
import { app, ipcMain } from "electron";
|
|
101
102
|
import { Effect } from "effect";
|
|
102
|
-
import
|
|
103
|
+
import * as Runtime from "effect/Runtime";
|
|
104
|
+
import { createRpcEndpoint, createEventPublisher } from "electron-effect-rpc/main";
|
|
103
105
|
import { contract, WorkUnitProgress } from "./contract.ts";
|
|
104
106
|
|
|
105
107
|
const implementations = {
|
|
106
108
|
GetAppVersion: () => Effect.succeed({ version: app.getVersion() }),
|
|
107
109
|
};
|
|
108
110
|
|
|
109
|
-
|
|
111
|
+
const endpoint = createRpcEndpoint(contract, ipcMain, implementations, {
|
|
112
|
+
runtime: Runtime.defaultRuntime,
|
|
113
|
+
});
|
|
114
|
+
endpoint.start();
|
|
110
115
|
|
|
111
|
-
const
|
|
116
|
+
const publisher = createEventPublisher(contract, {
|
|
112
117
|
getWindow: () => mainWindow,
|
|
113
118
|
});
|
|
119
|
+
publisher.start();
|
|
114
120
|
|
|
115
|
-
|
|
121
|
+
publisher.publish(WorkUnitProgress, {
|
|
116
122
|
requestId: "req-1",
|
|
117
123
|
chunk: "working...",
|
|
118
124
|
done: false,
|
|
119
125
|
});
|
|
120
126
|
```
|
|
121
127
|
|
|
122
|
-
|
|
128
|
+
Pass the runtime used to execute handler effects:
|
|
123
129
|
|
|
124
130
|
```ts
|
|
125
131
|
import * as Runtime from "effect/Runtime";
|
|
126
|
-
import {
|
|
132
|
+
import { createRpcEndpoint } from "electron-effect-rpc/main";
|
|
127
133
|
import { contract } from "./contract.ts";
|
|
128
134
|
|
|
129
|
-
|
|
135
|
+
const endpoint = createRpcEndpoint(contract, ipcMain, implementations, {
|
|
130
136
|
runtime: Runtime.defaultRuntime,
|
|
131
137
|
});
|
|
138
|
+
|
|
139
|
+
endpoint.start();
|
|
132
140
|
```
|
|
133
141
|
|
|
134
142
|
### Preload: expose bridge globals
|
|
@@ -152,13 +160,25 @@ exposeRpcBridge({
|
|
|
152
160
|
});
|
|
153
161
|
```
|
|
154
162
|
|
|
163
|
+
Or use low-level bridge adapters for custom exposure:
|
|
164
|
+
```ts
|
|
165
|
+
import { createBridgeAdapters } from "electron-effect-rpc/preload";
|
|
166
|
+
|
|
167
|
+
const bridge = createBridgeAdapters();
|
|
168
|
+
// bridge.invoke(method, payload)
|
|
169
|
+
// bridge.subscribe(name, handler)
|
|
170
|
+
```
|
|
171
|
+
|
|
155
172
|
### Renderer: create client and subscriber
|
|
156
173
|
```ts
|
|
157
174
|
import { createRpcClient, createEventSubscriber } from "electron-effect-rpc/renderer";
|
|
158
175
|
import { contract, WorkUnitProgress } from "./contract.ts";
|
|
159
176
|
|
|
160
177
|
const client = createRpcClient(contract, { invoke: window.rpc.invoke });
|
|
161
|
-
const events = createEventSubscriber(contract, {
|
|
178
|
+
const events = createEventSubscriber(contract, {
|
|
179
|
+
subscribe: window.events.subscribe,
|
|
180
|
+
decodeMode: "safe", // default
|
|
181
|
+
});
|
|
162
182
|
|
|
163
183
|
const { version } = await client.GetAppVersion();
|
|
164
184
|
|
|
@@ -194,8 +214,10 @@ import { createInvokeStub } from "electron-effect-rpc/testing";
|
|
|
194
214
|
import { contract } from "./contract.ts";
|
|
195
215
|
|
|
196
216
|
const invoke = createInvokeStub(async (method, payload) => {
|
|
197
|
-
|
|
198
|
-
|
|
217
|
+
return {
|
|
218
|
+
type: "success",
|
|
219
|
+
data: { version: "1.0.0" },
|
|
220
|
+
};
|
|
199
221
|
});
|
|
200
222
|
|
|
201
223
|
const client = createRpcClient(contract, { invoke });
|
|
@@ -210,7 +232,8 @@ expect(invoke.invocations).toEqual([
|
|
|
210
232
|
You can stub `IpcMainLike` and collect registered handlers:
|
|
211
233
|
|
|
212
234
|
```ts
|
|
213
|
-
import
|
|
235
|
+
import * as Runtime from "effect/Runtime";
|
|
236
|
+
import { createRpcEndpoint } from "electron-effect-rpc/main";
|
|
214
237
|
import type { IpcMainLike } from "electron-effect-rpc/types";
|
|
215
238
|
import { contract } from "./contract.ts";
|
|
216
239
|
|
|
@@ -219,9 +242,15 @@ const ipcMainStub: IpcMainLike = {
|
|
|
219
242
|
handle: (channel, handler) => {
|
|
220
243
|
handlers.set(channel, handler);
|
|
221
244
|
},
|
|
245
|
+
removeHandler: (channel) => {
|
|
246
|
+
handlers.delete(channel);
|
|
247
|
+
},
|
|
222
248
|
};
|
|
223
249
|
|
|
224
|
-
|
|
250
|
+
const endpoint = createRpcEndpoint(contract, ipcMainStub, implementations, {
|
|
251
|
+
runtime: Runtime.defaultRuntime,
|
|
252
|
+
});
|
|
253
|
+
endpoint.start();
|
|
225
254
|
```
|
|
226
255
|
|
|
227
256
|
## Error Handling
|
|
@@ -236,13 +265,13 @@ Entry points:
|
|
|
236
265
|
- `electron-effect-rpc/contract`
|
|
237
266
|
- `rpc`, `event`, `defineContract`, `exitSchemaFor`, `SchemaNoContext`, `NoError`
|
|
238
267
|
- `electron-effect-rpc/types`
|
|
239
|
-
- Type aliases such as `Implementations`, `RpcClient`, `
|
|
268
|
+
- Type aliases such as `Implementations`, `RpcClient`, `RpcEventPublisher`, `IpcMainLike`
|
|
240
269
|
- `electron-effect-rpc/main`
|
|
241
|
-
- `
|
|
270
|
+
- `createRpcEndpoint`, `createEventPublisher`
|
|
242
271
|
- `electron-effect-rpc/renderer`
|
|
243
272
|
- `createRpcClient`, `createEventSubscriber`, `RpcDefectError`
|
|
244
273
|
- `electron-effect-rpc/preload`
|
|
245
|
-
- `exposeRpcBridge`
|
|
274
|
+
- `exposeRpcBridge`, `createBridgeAdapters`
|
|
246
275
|
- `electron-effect-rpc/testing`
|
|
247
276
|
- `createInvokeStub`, `createDeferred`
|
|
248
277
|
|
package/dist/contract.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import * as S from "@effect/schema/Schema";
|
|
|
2
2
|
export type SchemaNoContext = S.Schema.AnyNoContext;
|
|
3
3
|
export declare const NoError: typeof S.Never;
|
|
4
4
|
export type NoError = typeof NoError;
|
|
5
|
-
export type ErrorSchema = SchemaNoContext |
|
|
5
|
+
export type ErrorSchema = SchemaNoContext | NoError;
|
|
6
|
+
export declare function isNoErrorSchema(schema: ErrorSchema): schema is NoError;
|
|
6
7
|
export interface RpcMethod<Name extends string, Req extends SchemaNoContext, Res extends SchemaNoContext, Err extends ErrorSchema = NoError> {
|
|
7
8
|
readonly name: Name;
|
|
8
9
|
readonly req: Req;
|
|
@@ -33,8 +34,8 @@ export interface RpcContract<Methods extends ReadonlyArray<AnyMethod>, Events ex
|
|
|
33
34
|
readonly methods: Methods;
|
|
34
35
|
readonly events: Events;
|
|
35
36
|
}
|
|
36
|
-
export declare
|
|
37
|
+
export declare function defineContract<const Methods extends ReadonlyArray<AnyMethod>, const Events extends ReadonlyArray<AnyEvent>>(input: {
|
|
37
38
|
readonly methods: Methods;
|
|
38
39
|
readonly events: Events;
|
|
39
|
-
})
|
|
40
|
+
}): RpcContract<Methods, Events>;
|
|
40
41
|
//# sourceMappingURL=contract.d.ts.map
|
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,uBAAuB,CAAC;AAE3C,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;AAEpD,eAAO,MAAM,OAAO,gBAAU,CAAC;AAC/B,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC;AAErC,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
|
package/dist/contract.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import * as S from "@effect/schema/Schema";
|
|
2
2
|
export const NoError = S.Never;
|
|
3
|
+
export function isNoErrorSchema(schema) {
|
|
4
|
+
return schema === NoError;
|
|
5
|
+
}
|
|
3
6
|
export function rpc(name, req, res, err = NoError) {
|
|
4
7
|
return { name, req, res, err };
|
|
5
8
|
}
|
|
@@ -11,7 +14,7 @@ export const exitSchemaFor = (method) => S.Exit({
|
|
|
11
14
|
failure: method.err,
|
|
12
15
|
defect: S.Defect,
|
|
13
16
|
});
|
|
14
|
-
|
|
17
|
+
function collectDuplicates(names) {
|
|
15
18
|
const counts = new Map();
|
|
16
19
|
const duplicates = [];
|
|
17
20
|
for (const name of names) {
|
|
@@ -22,8 +25,8 @@ const collectDuplicates = (names) => {
|
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
27
|
return duplicates;
|
|
25
|
-
}
|
|
26
|
-
export
|
|
28
|
+
}
|
|
29
|
+
export function defineContract(input) {
|
|
27
30
|
const { methods, events } = input;
|
|
28
31
|
if (!Array.isArray(methods)) {
|
|
29
32
|
throw new Error("RPC contract methods must be an array.");
|
|
@@ -40,4 +43,4 @@ export const defineContract = (input) => {
|
|
|
40
43
|
throw new Error(`Duplicate RPC event name(s): ${duplicateEvents.join(", ")}`);
|
|
41
44
|
}
|
|
42
45
|
return input;
|
|
43
|
-
}
|
|
46
|
+
}
|
package/dist/main.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type AnyEvent, type AnyMethod, type
|
|
3
|
-
export declare
|
|
4
|
-
export declare
|
|
1
|
+
import type { RpcContract } from "./contract.ts";
|
|
2
|
+
import { type AnyEvent, type AnyMethod, type EventPublisherOptions, type Implementations, type IpcMainLike, type RpcEndpoint, type RpcEndpointOptions, type RpcEventPublisher } from "./types.ts";
|
|
3
|
+
export declare function createRpcEndpoint<const Methods extends ReadonlyArray<AnyMethod>, const Events extends ReadonlyArray<AnyEvent>, R = never>(contract: RpcContract<Methods, Events>, ipc: IpcMainLike, implementations: Implementations<RpcContract<Methods, Events>, R>, options: RpcEndpointOptions<R>): RpcEndpoint;
|
|
4
|
+
export declare function createEventPublisher<const Methods extends ReadonlyArray<AnyMethod>, const Events extends ReadonlyArray<AnyEvent>>(_contract: RpcContract<Methods, Events>, options: EventPublisherOptions): RpcEventPublisher<RpcContract<Methods, Events>>;
|
|
5
5
|
//# sourceMappingURL=main.d.ts.map
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,WAAW,EAKZ,MAAM,eAAe,CAAC;AAQvB,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,SAAS,EAEd,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,YAAY,CAAC;AAmBpB,wBAAgB,iBAAiB,CAC/B,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAC5C,CAAC,GAAG,KAAK,EAET,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,EACtC,GAAG,EAAE,WAAW,EAChB,eAAe,EAAE,eAAe,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,EACjE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC7B,WAAW,CA4Kb;AAmBD,wBAAgB,oBAAoB,CAClC,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAE5C,SAAS,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,EACvC,OAAO,EAAE,qBAAqB,GAC7B,iBAAiB,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CA8KjD"}
|
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,276 @@ 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`);
|
|
40
63
|
}
|
|
41
|
-
const exit = await runPromiseExit(
|
|
42
|
-
|
|
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
|
+
}
|
|
95
|
+
}
|
|
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
|
+
for (const [channel, listener] of listeners) {
|
|
113
|
+
ipc.handle(channel, listener);
|
|
114
|
+
}
|
|
115
|
+
running = true;
|
|
116
|
+
}
|
|
117
|
+
function stop() {
|
|
118
|
+
if (!running) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
for (const channel of listeners.keys()) {
|
|
122
|
+
ipc.removeHandler(channel);
|
|
123
|
+
}
|
|
124
|
+
running = false;
|
|
125
|
+
}
|
|
126
|
+
function dispose() {
|
|
127
|
+
if (disposed) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
stop();
|
|
131
|
+
disposed = true;
|
|
132
|
+
}
|
|
133
|
+
function isRunning() {
|
|
134
|
+
return running;
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
start,
|
|
138
|
+
stop,
|
|
139
|
+
dispose,
|
|
140
|
+
isRunning,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function clampQueueSize(maxQueueSize) {
|
|
144
|
+
if (maxQueueSize === undefined) {
|
|
145
|
+
return 1000;
|
|
146
|
+
}
|
|
147
|
+
if (!Number.isFinite(maxQueueSize) || maxQueueSize < 1) {
|
|
148
|
+
throw new Error("Event publisher maxQueueSize must be a positive finite number.");
|
|
149
|
+
}
|
|
150
|
+
return Math.floor(maxQueueSize);
|
|
151
|
+
}
|
|
152
|
+
export function createEventPublisher(_contract, options) {
|
|
153
|
+
const channelPrefix = resolveChannelPrefix(options.channelPrefix);
|
|
154
|
+
const diagnostics = options.diagnostics;
|
|
155
|
+
const maxQueueSize = clampQueueSize(options.maxQueueSize);
|
|
156
|
+
const queue = [];
|
|
157
|
+
let dropped = 0;
|
|
158
|
+
let running = false;
|
|
159
|
+
let disposed = false;
|
|
160
|
+
let draining = false;
|
|
161
|
+
let drainScheduled = false;
|
|
162
|
+
function scheduleDrain() {
|
|
163
|
+
if (!running || disposed || draining || drainScheduled) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
drainScheduled = true;
|
|
167
|
+
queueMicrotask(() => {
|
|
168
|
+
drainScheduled = false;
|
|
169
|
+
drain();
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function dispatch(item) {
|
|
173
|
+
let encoded;
|
|
174
|
+
try {
|
|
175
|
+
encoded = S.encodeSync(item.event.payload)(item.payload);
|
|
176
|
+
}
|
|
177
|
+
catch (cause) {
|
|
178
|
+
dropped += 1;
|
|
179
|
+
safelyCall(diagnostics?.onDecodeFailure, {
|
|
180
|
+
scope: "event-payload",
|
|
181
|
+
name: item.event.name,
|
|
182
|
+
payload: item.payload,
|
|
183
|
+
cause,
|
|
184
|
+
});
|
|
185
|
+
safelyCall(diagnostics?.onDroppedEvent, {
|
|
186
|
+
event: item.event.name,
|
|
187
|
+
payload: item.payload,
|
|
188
|
+
reason: "encoding_failed",
|
|
189
|
+
queued: queue.length,
|
|
190
|
+
dropped,
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const window = options.getWindow();
|
|
195
|
+
if (!window || window.isDestroyed()) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
window.webContents.send(`${channelPrefix.event}${item.event.name}`, encoded);
|
|
200
|
+
}
|
|
201
|
+
catch (cause) {
|
|
202
|
+
safelyCall(diagnostics?.onDispatchFailure, {
|
|
203
|
+
event: item.event.name,
|
|
204
|
+
payload: item.payload,
|
|
205
|
+
cause,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function drain() {
|
|
210
|
+
if (!running || disposed || draining) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
draining = true;
|
|
214
|
+
try {
|
|
215
|
+
while (running && !disposed && queue.length > 0) {
|
|
216
|
+
const next = queue.shift();
|
|
217
|
+
if (!next) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
dispatch(next);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
draining = false;
|
|
225
|
+
if (running && !disposed && queue.length > 0) {
|
|
226
|
+
scheduleDrain();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function enqueue(item) {
|
|
231
|
+
if (queue.length >= maxQueueSize) {
|
|
232
|
+
const evicted = queue.shift();
|
|
233
|
+
dropped += 1;
|
|
234
|
+
if (evicted) {
|
|
235
|
+
safelyCall(diagnostics?.onDroppedEvent, {
|
|
236
|
+
event: evicted.event.name,
|
|
237
|
+
payload: evicted.payload,
|
|
238
|
+
reason: "queue_full",
|
|
239
|
+
queued: queue.length,
|
|
240
|
+
dropped,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
queue.push(item);
|
|
245
|
+
scheduleDrain();
|
|
246
|
+
}
|
|
247
|
+
function publish(event, payload) {
|
|
248
|
+
return Effect.sync(() => {
|
|
249
|
+
if (disposed) {
|
|
65
250
|
return;
|
|
66
251
|
}
|
|
67
|
-
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
252
|
+
enqueue({ event, payload });
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
function start() {
|
|
256
|
+
if (disposed) {
|
|
257
|
+
throw new Error("Event publisher has already been disposed.");
|
|
258
|
+
}
|
|
259
|
+
if (running) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
running = true;
|
|
263
|
+
scheduleDrain();
|
|
264
|
+
}
|
|
265
|
+
function stop() {
|
|
266
|
+
if (!running) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
running = false;
|
|
270
|
+
}
|
|
271
|
+
function dispose() {
|
|
272
|
+
if (disposed) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
stop();
|
|
276
|
+
queue.length = 0;
|
|
277
|
+
disposed = true;
|
|
278
|
+
}
|
|
279
|
+
function isRunning() {
|
|
280
|
+
return running;
|
|
281
|
+
}
|
|
282
|
+
function stats() {
|
|
283
|
+
return {
|
|
284
|
+
queued: queue.length,
|
|
285
|
+
dropped,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
publish,
|
|
290
|
+
start,
|
|
291
|
+
stop,
|
|
292
|
+
dispose,
|
|
293
|
+
isRunning,
|
|
294
|
+
stats,
|
|
295
|
+
};
|
|
296
|
+
}
|
package/dist/preload.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
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 declare
|
|
8
|
-
export
|
|
13
|
+
export declare function createBridgeAdapters(options?: BridgeAdaptersOptions): BridgeAdapters;
|
|
14
|
+
export declare function exposeRpcBridge(options?: BridgeExposureOptions): void;
|
|
9
15
|
//# 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":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"preload.d.ts","sourceRoot":"","sources":["../src/preload.ts"],"names":[],"mappings":"AACA,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,wBAAgB,oBAAoB,CAClC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAsBhB;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAerE"}
|
package/dist/preload.js
CHANGED
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import { contextBridge, ipcRenderer } from "electron";
|
|
2
|
-
import { defaultChannelPrefix } from "./types.js";
|
|
3
|
-
export
|
|
4
|
-
const rpcGlobal = options?.rpcGlobal ?? "rpc";
|
|
5
|
-
const eventsGlobal = options?.eventsGlobal ?? "events";
|
|
2
|
+
import { defaultChannelPrefix, } from "./types.js";
|
|
3
|
+
export function createBridgeAdapters(options) {
|
|
6
4
|
const channelPrefix = options?.channelPrefix ?? defaultChannelPrefix;
|
|
7
5
|
const invoke = (method, payload) => ipcRenderer.invoke(`${channelPrefix.rpc}${method}`, payload);
|
|
8
6
|
const subscribe = (event, listener) => {
|
|
9
7
|
const wrapped = (_event, payload) => listener(payload);
|
|
10
|
-
|
|
8
|
+
const channel = `${channelPrefix.event}${event}`;
|
|
9
|
+
ipcRenderer.on(channel, wrapped);
|
|
11
10
|
return () => {
|
|
12
|
-
ipcRenderer.removeListener(
|
|
11
|
+
ipcRenderer.removeListener(channel, wrapped);
|
|
13
12
|
};
|
|
14
13
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
return {
|
|
15
|
+
invoke,
|
|
16
|
+
subscribe,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function exposeRpcBridge(options) {
|
|
20
|
+
const rpcGlobal = options?.rpcGlobal ?? "rpc";
|
|
21
|
+
const eventsGlobal = options?.eventsGlobal ?? "events";
|
|
22
|
+
const adapters = createBridgeAdapters({
|
|
23
|
+
channelPrefix: options?.channelPrefix,
|
|
24
|
+
});
|
|
25
|
+
contextBridge.exposeInMainWorld(rpcGlobal, {
|
|
26
|
+
invoke: adapters.invoke,
|
|
27
|
+
});
|
|
28
|
+
contextBridge.exposeInMainWorld(eventsGlobal, {
|
|
29
|
+
subscribe: adapters.subscribe,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -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,CAAC,EAAE,gBAAgB,GACzB,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAqHzC;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,CAAC,EAAE,sBAAsB,GAC/B,eAAe,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CA+E/C;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/renderer.js
CHANGED
|
@@ -1,81 +1,171 @@
|
|
|
1
1
|
import * as S from "@effect/schema/Schema";
|
|
2
2
|
import { Cause, Exit } from "effect";
|
|
3
|
-
import { exitSchemaFor, } from "./contract.js";
|
|
3
|
+
import { exitSchemaFor, isNoErrorSchema, } from "./contract.js";
|
|
4
|
+
import { formatUnknown, parseRpcResponseEnvelope, safelyCall } from "./protocol.js";
|
|
4
5
|
import { RpcDefectError, } from "./types.js";
|
|
5
|
-
|
|
6
|
-
const requireInvoke = (options) => {
|
|
6
|
+
function requireInvoke(options) {
|
|
7
7
|
if (!options?.invoke) {
|
|
8
8
|
throw new Error("RpcClientOptions.invoke is required.");
|
|
9
9
|
}
|
|
10
10
|
return options.invoke;
|
|
11
|
-
}
|
|
12
|
-
|
|
11
|
+
}
|
|
12
|
+
function requireSubscribe(options) {
|
|
13
13
|
if (!options?.subscribe) {
|
|
14
14
|
throw new Error("EventSubscriberOptions.subscribe is required.");
|
|
15
15
|
}
|
|
16
16
|
return options.subscribe;
|
|
17
|
-
}
|
|
18
|
-
|
|
17
|
+
}
|
|
18
|
+
function decodeLegacyExit(method, raw) {
|
|
19
|
+
const exitSchema = exitSchemaFor(method);
|
|
20
|
+
const decodeExit = S.decodeUnknownSync(exitSchema);
|
|
21
|
+
const exit = decodeExit(raw);
|
|
22
|
+
if (Exit.isSuccess(exit)) {
|
|
23
|
+
return exit.value;
|
|
24
|
+
}
|
|
25
|
+
const failureOption = Cause.failureOption(exit.cause);
|
|
26
|
+
if (failureOption._tag === "Some") {
|
|
27
|
+
throw failureOption.value;
|
|
28
|
+
}
|
|
29
|
+
const defectOption = Cause.dieOption(exit.cause);
|
|
30
|
+
if (defectOption._tag === "Some") {
|
|
31
|
+
const defect = defectOption.value;
|
|
32
|
+
if (defect instanceof Error) {
|
|
33
|
+
throw new RpcDefectError(defect.message, defect);
|
|
34
|
+
}
|
|
35
|
+
throw new RpcDefectError(String(defect), defect);
|
|
36
|
+
}
|
|
37
|
+
throw new RpcDefectError("RPC call was interrupted or failed unexpectedly", exit.cause);
|
|
38
|
+
}
|
|
39
|
+
export function createRpcClient(contract, options) {
|
|
19
40
|
const invoke = requireInvoke(options);
|
|
41
|
+
const diagnostics = options?.diagnostics;
|
|
42
|
+
const decodeMode = options?.rpcDecodeMode ?? "envelope";
|
|
20
43
|
const call = async (method, input) => {
|
|
21
44
|
let encoded;
|
|
22
45
|
try {
|
|
23
46
|
encoded = S.encodeSync(method.req)(input);
|
|
24
47
|
}
|
|
25
48
|
catch (cause) {
|
|
26
|
-
|
|
49
|
+
safelyCall(diagnostics?.onDecodeFailure, {
|
|
50
|
+
scope: "rpc-request",
|
|
51
|
+
name: method.name,
|
|
52
|
+
payload: input,
|
|
53
|
+
cause,
|
|
54
|
+
});
|
|
55
|
+
throw new Error(`RPC ${method.name} request encoding failed: ${formatUnknown(cause)}`);
|
|
27
56
|
}
|
|
28
57
|
const raw = await invoke(method.name, encoded);
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
58
|
+
const envelope = parseRpcResponseEnvelope(raw);
|
|
59
|
+
if (envelope) {
|
|
60
|
+
switch (envelope.type) {
|
|
61
|
+
case "success":
|
|
62
|
+
try {
|
|
63
|
+
return S.decodeUnknownSync(method.res)(envelope.data);
|
|
64
|
+
}
|
|
65
|
+
catch (cause) {
|
|
66
|
+
safelyCall(diagnostics?.onDecodeFailure, {
|
|
67
|
+
scope: "rpc-response",
|
|
68
|
+
name: method.name,
|
|
69
|
+
payload: envelope.data,
|
|
70
|
+
cause,
|
|
71
|
+
});
|
|
72
|
+
throw new Error(`RPC ${method.name} success payload decoding failed: ${formatUnknown(cause)}`);
|
|
73
|
+
}
|
|
74
|
+
case "failure":
|
|
75
|
+
if (isNoErrorSchema(method.err)) {
|
|
76
|
+
throw new RpcDefectError(`RPC ${method.name} received a failure for a method that declares NoError`, envelope.error);
|
|
77
|
+
}
|
|
78
|
+
let decodedError;
|
|
79
|
+
try {
|
|
80
|
+
decodedError = S.decodeUnknownSync(method.err)(envelope.error.data);
|
|
81
|
+
}
|
|
82
|
+
catch (cause) {
|
|
83
|
+
safelyCall(diagnostics?.onDecodeFailure, {
|
|
84
|
+
scope: "rpc-response",
|
|
85
|
+
name: method.name,
|
|
86
|
+
payload: envelope.error,
|
|
87
|
+
cause,
|
|
88
|
+
});
|
|
89
|
+
throw new Error(`RPC ${method.name} failure payload decoding failed: ${formatUnknown(cause)}`);
|
|
90
|
+
}
|
|
91
|
+
throw decodedError;
|
|
92
|
+
case "defect":
|
|
93
|
+
throw new RpcDefectError(envelope.message, envelope.cause);
|
|
94
|
+
}
|
|
45
95
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
96
|
+
if (decodeMode === "dual") {
|
|
97
|
+
try {
|
|
98
|
+
return decodeLegacyExit(method, raw);
|
|
99
|
+
}
|
|
100
|
+
catch (cause) {
|
|
101
|
+
safelyCall(diagnostics?.onProtocolError, {
|
|
102
|
+
method: method.name,
|
|
103
|
+
response: raw,
|
|
104
|
+
cause,
|
|
105
|
+
});
|
|
106
|
+
throw cause;
|
|
51
107
|
}
|
|
52
|
-
throw new RpcDefectError(String(defect), defect);
|
|
53
108
|
}
|
|
54
|
-
|
|
109
|
+
const cause = new Error(`RPC ${method.name} response was not a valid envelope.`);
|
|
110
|
+
safelyCall(diagnostics?.onProtocolError, {
|
|
111
|
+
method: method.name,
|
|
112
|
+
response: raw,
|
|
113
|
+
cause,
|
|
114
|
+
});
|
|
115
|
+
throw cause;
|
|
55
116
|
};
|
|
56
117
|
const client = Object.create(null);
|
|
57
118
|
const clientRecord = client;
|
|
58
|
-
contract.methods
|
|
119
|
+
for (const method of contract.methods) {
|
|
59
120
|
const caller = (input) => {
|
|
60
121
|
const payload = input ?? S.decodeUnknownSync(method.req)({});
|
|
61
122
|
return call(method, payload);
|
|
62
123
|
};
|
|
63
124
|
clientRecord[method.name] = caller;
|
|
64
|
-
}
|
|
125
|
+
}
|
|
65
126
|
return client;
|
|
66
|
-
}
|
|
67
|
-
|
|
127
|
+
}
|
|
128
|
+
function reportDecodeFailure(mode, eventName, payload, cause, options) {
|
|
129
|
+
safelyCall(options?.diagnostics?.onDecodeFailure, {
|
|
130
|
+
scope: "event-payload",
|
|
131
|
+
name: eventName,
|
|
132
|
+
payload,
|
|
133
|
+
cause,
|
|
134
|
+
});
|
|
135
|
+
if (mode === "strict") {
|
|
136
|
+
throw cause;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export function createEventSubscriber(contract, options) {
|
|
68
140
|
const subscribe = requireSubscribe(options);
|
|
141
|
+
const mode = options?.decodeMode ?? "safe";
|
|
69
142
|
const eventMap = new Map();
|
|
143
|
+
const subscriptions = new Set();
|
|
70
144
|
for (const event of contract.events) {
|
|
71
145
|
eventMap.set(event.name, event);
|
|
72
146
|
}
|
|
147
|
+
function registerUnsubscribe(unsubscribe) {
|
|
148
|
+
subscriptions.add(unsubscribe);
|
|
149
|
+
return () => {
|
|
150
|
+
if (subscriptions.delete(unsubscribe)) {
|
|
151
|
+
unsubscribe();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
73
155
|
const subscribeEvent = (event, handler) => {
|
|
74
156
|
const decoder = S.decodeUnknownSync(event.payload);
|
|
75
|
-
|
|
76
|
-
|
|
157
|
+
const unsubscribe = subscribe(event.name, (payload) => {
|
|
158
|
+
let decoded;
|
|
159
|
+
try {
|
|
160
|
+
decoded = decoder(payload);
|
|
161
|
+
}
|
|
162
|
+
catch (cause) {
|
|
163
|
+
reportDecodeFailure(mode, event.name, payload, cause, options);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
77
166
|
handler(decoded);
|
|
78
167
|
});
|
|
168
|
+
return registerUnsubscribe(unsubscribe);
|
|
79
169
|
};
|
|
80
170
|
const subscribeByName = (name, handler) => {
|
|
81
171
|
const event = eventMap.get(name);
|
|
@@ -83,11 +173,29 @@ export const createEventSubscriber = (contract, options) => {
|
|
|
83
173
|
throw new Error(`Unknown event: ${name}`);
|
|
84
174
|
}
|
|
85
175
|
const decoder = S.decodeUnknownSync(event.payload);
|
|
86
|
-
|
|
176
|
+
const unsubscribe = subscribe(name, (payload) => {
|
|
177
|
+
let decoded;
|
|
178
|
+
try {
|
|
179
|
+
decoded = decoder(payload);
|
|
180
|
+
}
|
|
181
|
+
catch (cause) {
|
|
182
|
+
reportDecodeFailure(mode, name, payload, cause, options);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
handler(decoded);
|
|
186
|
+
});
|
|
187
|
+
return registerUnsubscribe(unsubscribe);
|
|
87
188
|
};
|
|
189
|
+
function dispose() {
|
|
190
|
+
for (const unsubscribe of subscriptions) {
|
|
191
|
+
unsubscribe();
|
|
192
|
+
}
|
|
193
|
+
subscriptions.clear();
|
|
194
|
+
}
|
|
88
195
|
return {
|
|
89
196
|
subscribe: subscribeEvent,
|
|
90
197
|
subscribeByName,
|
|
198
|
+
dispose,
|
|
91
199
|
};
|
|
92
|
-
}
|
|
200
|
+
}
|
|
93
201
|
export { RpcDefectError } from "./types.js";
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type * as Effect from "effect/Effect";
|
|
2
2
|
import type * as Runtime from "effect/Runtime";
|
|
3
|
-
import type { BrowserWindow } from "electron";
|
|
4
3
|
import type { AnyEvent, AnyMethod, ExtractMethod, RpcContract, RpcError, RpcEventPayload, RpcInput, RpcOutput } from "./contract.ts";
|
|
5
4
|
export type { AnyEvent, AnyMethod, ErrorSchema, ExtractMethod, RpcContract, RpcError, RpcEvent, RpcEventPayload, RpcInput, RpcMethod, RpcOutput, SchemaNoContext, } from "./contract.ts";
|
|
6
5
|
export type Implementations<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[]>, R = never> = {
|
|
@@ -21,32 +20,105 @@ export type ChannelPrefix = {
|
|
|
21
20
|
readonly event: string;
|
|
22
21
|
};
|
|
23
22
|
export declare const defaultChannelPrefix: ChannelPrefix;
|
|
24
|
-
export type
|
|
25
|
-
|
|
23
|
+
export type DecodeFailureScope = "rpc-request" | "rpc-response" | "event-payload";
|
|
24
|
+
export type DecodeFailureContext = {
|
|
25
|
+
readonly scope: DecodeFailureScope;
|
|
26
|
+
readonly name: string;
|
|
27
|
+
readonly payload: unknown;
|
|
28
|
+
readonly cause: unknown;
|
|
29
|
+
};
|
|
30
|
+
export type ProtocolErrorContext = {
|
|
31
|
+
readonly method: string;
|
|
32
|
+
readonly response: unknown;
|
|
33
|
+
readonly cause: unknown;
|
|
34
|
+
};
|
|
35
|
+
export type DispatchFailureContext = {
|
|
36
|
+
readonly event: string;
|
|
37
|
+
readonly payload: unknown;
|
|
38
|
+
readonly cause: unknown;
|
|
39
|
+
};
|
|
40
|
+
export type DroppedEventReason = "queue_full" | "encoding_failed";
|
|
41
|
+
export type DroppedEventContext = {
|
|
42
|
+
readonly event: string;
|
|
43
|
+
readonly payload: unknown;
|
|
44
|
+
readonly reason: DroppedEventReason;
|
|
45
|
+
readonly queued: number;
|
|
46
|
+
readonly dropped: number;
|
|
26
47
|
};
|
|
27
48
|
export type RpcInvoke = (method: string, payload: unknown) => Promise<unknown>;
|
|
28
|
-
|
|
29
|
-
export type
|
|
30
|
-
readonly
|
|
31
|
-
readonly
|
|
49
|
+
export type RpcResponseDecodeMode = "envelope" | "dual";
|
|
50
|
+
export type RpcClientDiagnostics = {
|
|
51
|
+
readonly onDecodeFailure?: (context: DecodeFailureContext) => void;
|
|
52
|
+
readonly onProtocolError?: (context: ProtocolErrorContext) => void;
|
|
32
53
|
};
|
|
33
54
|
export type RpcClientOptions = {
|
|
34
55
|
readonly invoke?: RpcInvoke;
|
|
35
|
-
readonly
|
|
56
|
+
readonly diagnostics?: RpcClientDiagnostics;
|
|
57
|
+
readonly rpcDecodeMode?: RpcResponseDecodeMode;
|
|
36
58
|
};
|
|
37
|
-
export type
|
|
59
|
+
export type RpcEndpointDiagnostics = {
|
|
60
|
+
readonly onDecodeFailure?: (context: DecodeFailureContext) => void;
|
|
61
|
+
readonly onProtocolError?: (context: ProtocolErrorContext) => void;
|
|
62
|
+
};
|
|
63
|
+
export type IpcMainLike = {
|
|
64
|
+
readonly handle: (channel: string, listener: (event: unknown, payload: unknown) => unknown) => unknown;
|
|
65
|
+
readonly removeHandler: (channel: string) => unknown;
|
|
66
|
+
};
|
|
67
|
+
export interface RpcEndpoint {
|
|
68
|
+
readonly start: () => void;
|
|
69
|
+
readonly stop: () => void;
|
|
70
|
+
readonly dispose: () => void;
|
|
71
|
+
readonly isRunning: () => boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Runtime used to execute handler effects.
|
|
75
|
+
*/
|
|
76
|
+
export type RpcEndpointOptions<R = never> = {
|
|
38
77
|
readonly channelPrefix?: ChannelPrefix;
|
|
39
|
-
readonly
|
|
78
|
+
readonly runtime: Runtime.Runtime<R>;
|
|
79
|
+
readonly diagnostics?: RpcEndpointDiagnostics;
|
|
40
80
|
};
|
|
41
|
-
export type
|
|
81
|
+
export type EventPublisherDiagnostics = {
|
|
82
|
+
readonly onDecodeFailure?: (context: DecodeFailureContext) => void;
|
|
83
|
+
readonly onDispatchFailure?: (context: DispatchFailureContext) => void;
|
|
84
|
+
readonly onDroppedEvent?: (context: DroppedEventContext) => void;
|
|
85
|
+
};
|
|
86
|
+
export type RendererWindowLike = {
|
|
87
|
+
readonly isDestroyed: () => boolean;
|
|
88
|
+
readonly webContents: {
|
|
89
|
+
readonly send: (channel: string, payload: unknown) => void;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
export type EventPublisherOptions = {
|
|
42
93
|
readonly channelPrefix?: ChannelPrefix;
|
|
43
|
-
readonly
|
|
94
|
+
readonly getWindow: () => RendererWindowLike | null;
|
|
95
|
+
readonly maxQueueSize?: number;
|
|
96
|
+
readonly diagnostics?: EventPublisherDiagnostics;
|
|
44
97
|
};
|
|
45
|
-
export interface
|
|
46
|
-
readonly
|
|
98
|
+
export interface RpcEventPublisher<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[]>> {
|
|
99
|
+
readonly publish: <E extends C["events"][number]>(event: E, payload: RpcEventPayload<E>) => Effect.Effect<void, never>;
|
|
100
|
+
readonly start: () => void;
|
|
101
|
+
readonly stop: () => void;
|
|
102
|
+
readonly dispose: () => void;
|
|
103
|
+
readonly isRunning: () => boolean;
|
|
104
|
+
readonly stats: () => {
|
|
105
|
+
readonly queued: number;
|
|
106
|
+
readonly dropped: number;
|
|
107
|
+
};
|
|
47
108
|
}
|
|
109
|
+
export type EventDecodeMode = "safe" | "strict";
|
|
110
|
+
export type EventSubscribe = (name: string, handler: (payload: unknown) => void) => () => void;
|
|
111
|
+
export type EventSubscriberDiagnostics = {
|
|
112
|
+
readonly onDecodeFailure?: (context: DecodeFailureContext) => void;
|
|
113
|
+
};
|
|
114
|
+
export type EventSubscriberOptions = {
|
|
115
|
+
readonly subscribe?: EventSubscribe;
|
|
116
|
+
readonly decodeMode?: EventDecodeMode;
|
|
117
|
+
readonly diagnostics?: EventSubscriberDiagnostics;
|
|
118
|
+
};
|
|
48
119
|
export interface EventSubscriber<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[]>> {
|
|
49
120
|
readonly subscribe: <E extends C["events"][number]>(event: E, handler: (payload: RpcEventPayload<E>) => void) => () => void;
|
|
50
|
-
readonly subscribeByName: (name:
|
|
121
|
+
readonly subscribeByName: (name: string, handler: (payload: unknown) => void) => () => void;
|
|
122
|
+
readonly dispose: () => void;
|
|
51
123
|
}
|
|
52
124
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|