electron-effect-rpc 0.7.1 → 0.9.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 +82 -0
- package/README.md +81 -46
- package/dist/contract.d.ts +5 -7
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +6 -4
- package/dist/kit.d.ts +26 -6
- package/dist/kit.d.ts.map +1 -1
- package/dist/kit.js +21 -7
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +55 -20
- package/dist/preload-bridge.d.ts +34 -0
- package/dist/preload-bridge.d.ts.map +1 -0
- package/dist/preload-bridge.js +85 -0
- package/dist/preload.d.ts +1 -16
- package/dist/preload.d.ts.map +1 -1
- package/dist/preload.js +6 -70
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +23 -5
- package/dist/types.d.ts +41 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +11 -0
- package/package.json +9 -6
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
|
|
7
|
+
- Schemas now come from `effect/Schema` (Effect core) instead of the deprecated
|
|
8
|
+
`@effect/schema` package. The `@effect/schema` peer dependency was removed and
|
|
9
|
+
the `effect` peer floor raised to `>=3.10.0`. Update contract imports:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// before
|
|
13
|
+
import * as S from "@effect/schema/Schema";
|
|
14
|
+
// after
|
|
15
|
+
import * as S from "effect/Schema";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- `event()` no longer accepts a third `context` schema argument. It was never
|
|
19
|
+
read by the publisher or subscriber; remove it from any `event(...)` calls.
|
|
20
|
+
- `channelPrefix.rpc` and `channelPrefix.event` must now differ; identical
|
|
21
|
+
prefixes throw at construction time.
|
|
22
|
+
- Providing `streamHandlers` when the contract defines no `streamMethods` now
|
|
23
|
+
throws instead of being silently ignored.
|
|
24
|
+
- Methods whose request schema type is `object` (e.g. `S.Object`) now require
|
|
25
|
+
an argument at the call site; previously the client generated a zero-arg
|
|
26
|
+
caller that always sent `{}`.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Handlers (unary and stream) receive an optional second `context` argument:
|
|
31
|
+
`{ sender: WebContentsLike | null }`.
|
|
32
|
+
- `EventSubscriber.stream(event)` — Effect-native event consumption as a
|
|
33
|
+
`Stream` that unsubscribes when its scope closes.
|
|
34
|
+
- `ipc.renderer(bridge, { diagnostics })` — renderer-side diagnostics hooks for
|
|
35
|
+
RPC, events, and streams in kit mode.
|
|
36
|
+
- `assertValidChannelPrefix` exported from `electron-effect-rpc/types`.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- Main-side stream fibers no longer leak when the renderer goes away without
|
|
41
|
+
cancelling: a destroyed sender now terminates the pipeline on the next chunk,
|
|
42
|
+
and when the real Electron `WebContents` emitter is available the fiber is
|
|
43
|
+
interrupted immediately on `destroyed`.
|
|
44
|
+
- Normally-completed streams no longer send a redundant `stream-cancel` invoke.
|
|
45
|
+
- `isNoErrorSchema` now matches `Never` schemas by AST tag, so a duplicated
|
|
46
|
+
schema package instance can't silently turn typed failures into defects.
|
|
47
|
+
- Stream failure-encoding defects now carry the encoding error message instead
|
|
48
|
+
of `[object Object]`.
|
|
49
|
+
|
|
50
|
+
## 0.8.0 and earlier
|
|
51
|
+
|
|
52
|
+
`getWindow` was renamed to `getWindows` and now returns an array, enabling
|
|
53
|
+
multi-window event fan-out. Empty array replaces `null` for "no windows."
|
|
54
|
+
|
|
55
|
+
Before:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
getWindow: () => mainWindow,
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
After:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
getWindows: () => [mainWindow],
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Renderer RPC methods now return `Effect.Effect` instead of `Promise`, and
|
|
68
|
+
`IpcMainHandle.emit` was removed in favor of `publish`.
|
|
69
|
+
|
|
70
|
+
Before:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const result = await client.GetAppVersion();
|
|
74
|
+
await mainRpc.emit(WorkUnitProgress, payload);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
After:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const result = await Effect.runPromise(client.GetAppVersion());
|
|
81
|
+
await Effect.runPromise(mainRpc.publish(WorkUnitProgress, payload));
|
|
82
|
+
```
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# electron-effect-rpc
|
|
2
2
|
|
|
3
|
-
Typed IPC RPC for Electron, built on Effect and
|
|
3
|
+
Typed IPC RPC for Electron, built on Effect and `effect/Schema`.
|
|
4
4
|
|
|
5
5
|
The ergonomic default is now a single shared `createIpcKit` configuration that
|
|
6
6
|
you reuse in main, preload, and renderer code. Low-level subpath APIs still
|
|
@@ -24,12 +24,12 @@ ESM-capable build pipeline.
|
|
|
24
24
|
|
|
25
25
|
- Electron with context isolation enabled.
|
|
26
26
|
- ESM-capable bundling.
|
|
27
|
-
- Peer dependencies: `effect
|
|
27
|
+
- Peer dependencies: `effect` (>=3.10), `electron`.
|
|
28
28
|
|
|
29
29
|
## Installation
|
|
30
30
|
|
|
31
31
|
```sh
|
|
32
|
-
bun add electron-effect-rpc effect
|
|
32
|
+
bun add electron-effect-rpc effect
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
## Quickstart (Kit-First)
|
|
@@ -37,7 +37,7 @@ bun add electron-effect-rpc effect @effect/schema
|
|
|
37
37
|
### 1) Define contract and kit once
|
|
38
38
|
|
|
39
39
|
```ts
|
|
40
|
-
import * as S from "
|
|
40
|
+
import * as S from "effect/Schema";
|
|
41
41
|
import { createIpcKit, defineContract, event, rpc, streamRpc } from "electron-effect-rpc";
|
|
42
42
|
|
|
43
43
|
export const GetAppVersion = rpc("GetAppVersion", S.Struct({}), S.Struct({ version: S.String }));
|
|
@@ -74,15 +74,22 @@ export const ipc = createIpcKit({
|
|
|
74
74
|
### 2) Main process
|
|
75
75
|
|
|
76
76
|
```ts
|
|
77
|
-
import
|
|
77
|
+
import path from "node:path";
|
|
78
|
+
import { app, BrowserWindow, ipcMain } from "electron";
|
|
78
79
|
import { Effect, Stream } from "effect";
|
|
79
80
|
import * as Runtime from "effect/Runtime";
|
|
80
81
|
import { ipc, WorkUnitProgress } from "./shared-ipc.ts";
|
|
81
82
|
|
|
83
|
+
const mainWindow = new BrowserWindow({
|
|
84
|
+
webPreferences: { preload: path.join(import.meta.dirname, "preload.js") },
|
|
85
|
+
});
|
|
86
|
+
|
|
82
87
|
const mainRpc = ipc.main({
|
|
83
88
|
ipcMain,
|
|
84
89
|
handlers: {
|
|
85
90
|
GetAppVersion: () => Effect.succeed({ version: app.getVersion() }),
|
|
91
|
+
// Handlers may take an optional second argument with request context:
|
|
92
|
+
// GetAppVersion: (_input, { sender }) => ...
|
|
86
93
|
},
|
|
87
94
|
streamHandlers: {
|
|
88
95
|
StreamAiGeneration: ({ prompt }) =>
|
|
@@ -108,12 +115,23 @@ void Effect.runPromise(
|
|
|
108
115
|
```ts
|
|
109
116
|
import { ipc } from "./shared-ipc.ts";
|
|
110
117
|
|
|
111
|
-
const { expose } =
|
|
118
|
+
const { expose } = ipc.preload();
|
|
112
119
|
expose();
|
|
113
120
|
```
|
|
114
121
|
|
|
115
122
|
This exposes one global by default: `window.api`.
|
|
116
123
|
|
|
124
|
+
If your preload runtime is ESM-only and does not expose synchronous `require`,
|
|
125
|
+
pass the imported Electron module explicitly:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import * as electron from "electron";
|
|
129
|
+
import { ipc } from "./shared-ipc.ts";
|
|
130
|
+
|
|
131
|
+
const { expose } = ipc.preload({ electronModule: electron });
|
|
132
|
+
expose();
|
|
133
|
+
```
|
|
134
|
+
|
|
117
135
|
### 4) Renderer
|
|
118
136
|
|
|
119
137
|
```ts
|
|
@@ -134,25 +152,44 @@ const unsubscribe = events.subscribe(WorkUnitProgress, (payload) => {
|
|
|
134
152
|
console.log(payload.chunk);
|
|
135
153
|
});
|
|
136
154
|
|
|
155
|
+
// Or consume events as an Effect Stream (unsubscribes when the scope closes):
|
|
156
|
+
await Effect.runPromise(
|
|
157
|
+
events
|
|
158
|
+
.stream(WorkUnitProgress)
|
|
159
|
+
.pipe(Stream.runForEach((payload) => Effect.sync(() => console.log(payload.chunk)))),
|
|
160
|
+
);
|
|
161
|
+
|
|
137
162
|
// later
|
|
138
163
|
unsubscribe();
|
|
139
164
|
dispose();
|
|
140
165
|
```
|
|
141
166
|
|
|
167
|
+
Renderer-side diagnostics hooks are available through the second argument:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const renderer = ipc.renderer(window.api, {
|
|
171
|
+
diagnostics: {
|
|
172
|
+
rpc: { onDecodeFailure: (ctx) => console.warn("rpc decode failure", ctx) },
|
|
173
|
+
events: { onDecodeFailure: (ctx) => console.warn("event decode failure", ctx) },
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
142
178
|
### 5) Window typing
|
|
143
179
|
|
|
180
|
+
Use the exported `IpcBridgeGlobal` helper so the declared shape stays in sync
|
|
181
|
+
with what `ipc.preload()` exposes:
|
|
182
|
+
|
|
144
183
|
```ts
|
|
184
|
+
import type { IpcBridgeGlobal } from "electron-effect-rpc";
|
|
185
|
+
|
|
145
186
|
declare global {
|
|
146
|
-
interface Window {
|
|
147
|
-
api: {
|
|
148
|
-
invoke: (method: string, payload: unknown) => Promise<unknown>;
|
|
149
|
-
subscribe: (name: string, handler: (payload: unknown) => void) => () => void;
|
|
150
|
-
onStreamFrame?: (listener: (frame: unknown) => void) => () => void;
|
|
151
|
-
};
|
|
152
|
-
}
|
|
187
|
+
interface Window extends IpcBridgeGlobal {}
|
|
153
188
|
}
|
|
154
189
|
```
|
|
155
190
|
|
|
191
|
+
For a custom global name, use `IpcBridgeGlobal<"myBridge">`.
|
|
192
|
+
|
|
156
193
|
## Error Model
|
|
157
194
|
|
|
158
195
|
Domain failures are modeled with tagged error schemas and are surfaced in the
|
|
@@ -166,39 +203,37 @@ which includes a stable `code` discriminator:
|
|
|
166
203
|
`stream_invoke_failed`, `stream_handshake_invalid`,
|
|
167
204
|
`stream_chunk_decode_failed`, and `stream_error_decode_failed`.
|
|
168
205
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
`
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
await Effect.runPromise(mainRpc.publish(WorkUnitProgress, payload));
|
|
201
|
-
```
|
|
206
|
+
Defect envelopes carry the main-process error message verbatim across IPC. If
|
|
207
|
+
any window in your app loads remote or less-trusted content, treat that as
|
|
208
|
+
information disclosure: catch and sanitize errors in your handlers rather than
|
|
209
|
+
letting raw exceptions (paths, query fragments) become defects.
|
|
210
|
+
|
|
211
|
+
## Operational Notes
|
|
212
|
+
|
|
213
|
+
**Stream backpressure.** Stream frames are pushed from main as fast as the
|
|
214
|
+
handler produces them; there is no acknowledgment across the IPC boundary. The
|
|
215
|
+
renderer buffers with `bufferSize: "unbounded"` by default, which is lossless
|
|
216
|
+
but means a fast producer with a slow consumer grows renderer memory. Bounded
|
|
217
|
+
buffers (`dropping`/`sliding`) are lossy by design and, with current Effect
|
|
218
|
+
`Stream.asyncPush` internals, can lose terminal signals under sustained
|
|
219
|
+
pressure. Rate-limit fast producers in the handler (`Stream.throttle`,
|
|
220
|
+
batching) rather than relying on a bounded renderer buffer.
|
|
221
|
+
|
|
222
|
+
**Unary RPC cancellation.** Interrupting the renderer-side Effect of a
|
|
223
|
+
client call (e.g. via `Effect.timeout`) does not abort the main-side handler;
|
|
224
|
+
the underlying `ipcRenderer.invoke` is not abortable. The handler runs to
|
|
225
|
+
completion and its response is discarded. Streaming RPC does propagate
|
|
226
|
+
cancellation to main. If a unary handler does expensive work, model it as a
|
|
227
|
+
stream or build explicit cancellation into your contract.
|
|
228
|
+
|
|
229
|
+
**Renderer teardown.** Main interrupts a stream's fiber when the renderer's
|
|
230
|
+
`webContents` is destroyed (immediately when the real Electron `WebContents`
|
|
231
|
+
event emitter is available, otherwise on the next chunk), so handler fibers do
|
|
232
|
+
not outlive closed windows.
|
|
233
|
+
|
|
234
|
+
## Migration Notes
|
|
235
|
+
|
|
236
|
+
See [CHANGELOG.md](./CHANGELOG.md) for breaking changes between versions.
|
|
202
237
|
|
|
203
238
|
## Low-Level APIs (Still Supported)
|
|
204
239
|
|
package/dist/contract.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as S from "
|
|
1
|
+
import * as S from "effect/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;
|
|
@@ -12,14 +12,12 @@ export interface RpcMethod<Name extends string, Req extends SchemaNoContext, Res
|
|
|
12
12
|
}
|
|
13
13
|
export declare function rpc<const Name extends string, Req extends SchemaNoContext, Res extends SchemaNoContext, Err extends ErrorSchema>(name: Name, req: Req, res: Res, err: Err): RpcMethod<Name, Req, Res, Err>;
|
|
14
14
|
export declare function rpc<const Name extends string, Req extends SchemaNoContext, Res extends SchemaNoContext>(name: Name, req: Req, res: Res): RpcMethod<Name, Req, Res, NoError>;
|
|
15
|
-
export interface RpcEvent<Payload extends SchemaNoContext,
|
|
15
|
+
export interface RpcEvent<Payload extends SchemaNoContext, Name extends string = string> {
|
|
16
16
|
readonly name: Name;
|
|
17
17
|
readonly payload: Payload;
|
|
18
|
-
readonly context: Context;
|
|
19
18
|
}
|
|
20
|
-
export declare function event<const Name extends string, Payload extends SchemaNoContext
|
|
21
|
-
export declare
|
|
22
|
-
export declare const exitSchemaFor: <Name extends string, Req extends SchemaNoContext, Res extends SchemaNoContext, Err extends ErrorSchema>(method: RpcMethod<Name, Req, Res, Err>) => S.Exit<Res, Err, S.Defect>;
|
|
19
|
+
export declare function event<const Name extends string, Payload extends SchemaNoContext>(name: Name, payload: Payload): RpcEvent<Payload, Name>;
|
|
20
|
+
export declare const exitSchemaFor: <Name extends string, Req extends SchemaNoContext, Res extends SchemaNoContext, Err extends ErrorSchema>(method: RpcMethod<Name, Req, Res, Err>) => S.Exit<Res, Err, typeof S.Defect>;
|
|
23
21
|
export interface StreamRpcMethod<Name extends string, Req extends SchemaNoContext, Chunk extends SchemaNoContext, Err extends ErrorSchema = NoError> {
|
|
24
22
|
readonly _tag: "StreamRpcMethod";
|
|
25
23
|
readonly name: Name;
|
|
@@ -37,7 +35,7 @@ export type ExtractStreamMethod<Methods extends readonly AnyStreamMethod[], Name
|
|
|
37
35
|
readonly name: Name;
|
|
38
36
|
}>;
|
|
39
37
|
export type AnyMethod = RpcMethod<string, SchemaNoContext, SchemaNoContext, ErrorSchema>;
|
|
40
|
-
export type AnyEvent = RpcEvent<SchemaNoContext,
|
|
38
|
+
export type AnyEvent = RpcEvent<SchemaNoContext, string>;
|
|
41
39
|
export type RpcInput<M extends AnyMethod> = S.Schema.Type<M["req"]>;
|
|
42
40
|
export type RpcOutput<M extends AnyMethod> = S.Schema.Type<M["res"]>;
|
|
43
41
|
export type RpcError<M extends AnyMethod> = S.Schema.Type<M["err"]>;
|
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,
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
|
package/dist/contract.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import * as S from "
|
|
1
|
+
import * as S from "effect/Schema";
|
|
2
2
|
export const NoError = S.Never;
|
|
3
3
|
export function isNoErrorSchema(schema) {
|
|
4
|
-
|
|
4
|
+
// Also match by AST tag so a Never schema from a second copy of the
|
|
5
|
+
// schema package (npm dedup failure) is still treated as NoError.
|
|
6
|
+
return schema === NoError || schema.ast._tag === "NeverKeyword";
|
|
5
7
|
}
|
|
6
8
|
export function rpc(name, req, res, err = NoError) {
|
|
7
9
|
return { name, req, res, err };
|
|
8
10
|
}
|
|
9
|
-
export function event(name, payload
|
|
10
|
-
return { name, payload
|
|
11
|
+
export function event(name, payload) {
|
|
12
|
+
return { name, payload };
|
|
11
13
|
}
|
|
12
14
|
export const exitSchemaFor = (method) => S.Exit({
|
|
13
15
|
success: method.res,
|
package/dist/kit.d.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import type * as Runtime from "effect/Runtime";
|
|
3
3
|
import type { AnyEvent, AnyMethod, AnyStreamMethod, RpcContract, RpcEventPayload } from "./contract.ts";
|
|
4
|
-
import { type ChannelPrefix, type EventDecodeMode, type EventPublisherDiagnostics, type EventSubscribe, type EventSubscriber, type IpcMainLike, type Implementations, type OnStreamFrame, type RendererWindowLike, type RpcClient, type RpcEndpoint, type RpcEndpointDiagnostics, type RpcEventPublisher, type RpcInvoke, type RpcResponseDecodeMode, type StreamImplementations, type StreamRpcClient, type StreamBufferOptions } from "./types.ts";
|
|
4
|
+
import { type ChannelPrefix, type EventDecodeMode, type EventPublisherDiagnostics, type EventSubscribe, type EventSubscriber, type EventSubscriberDiagnostics, type IpcMainLike, type Implementations, type OnStreamFrame, type RendererWindowLike, type RpcClient, type RpcClientDiagnostics, type RpcEndpoint, type RpcEndpointDiagnostics, type RpcEventPublisher, type RpcInvoke, type RpcResponseDecodeMode, type StreamImplementations, type StreamRpcClient, type StreamBufferOptions } from "./types.ts";
|
|
5
|
+
/**
|
|
6
|
+
* Narrow surface the preload script exposes to the renderer.
|
|
7
|
+
*
|
|
8
|
+
* Implementations are responsible for applying the kit's channel prefixes:
|
|
9
|
+
* `invoke(method, payload)` must call `ipcRenderer.invoke(channelPrefix.rpc +
|
|
10
|
+
* method, payload)`, `subscribe(name, ...)` must listen on
|
|
11
|
+
* `channelPrefix.event + name`, and `onStreamFrame` must listen on
|
|
12
|
+
* `channelPrefix.rpc + "sf"`. The bridge produced by `ipc.preload()` does this
|
|
13
|
+
* for you; hand-written bridges that skip the prefixes fail with
|
|
14
|
+
* `invoke_failed` defects at runtime.
|
|
15
|
+
*/
|
|
5
16
|
export type IpcBridge = {
|
|
6
17
|
readonly invoke: RpcInvoke;
|
|
7
18
|
readonly subscribe: EventSubscribe;
|
|
@@ -34,6 +45,17 @@ type IpcMainOptions<C extends RpcContract<readonly AnyMethod[], readonly AnyEven
|
|
|
34
45
|
readonly events?: EventPublisherDiagnostics;
|
|
35
46
|
};
|
|
36
47
|
};
|
|
48
|
+
type IpcPreloadOptions = {
|
|
49
|
+
readonly global?: string;
|
|
50
|
+
readonly electronModule?: unknown;
|
|
51
|
+
};
|
|
52
|
+
type IpcRendererOptions = {
|
|
53
|
+
readonly diagnostics?: {
|
|
54
|
+
readonly rpc?: RpcClientDiagnostics;
|
|
55
|
+
readonly events?: EventSubscriberDiagnostics;
|
|
56
|
+
readonly stream?: RpcClientDiagnostics;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
37
59
|
export type IpcMainHandle<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[], readonly AnyStreamMethod[]>> = {
|
|
38
60
|
readonly endpoint: RpcEndpoint;
|
|
39
61
|
readonly publisher: RpcEventPublisher<C>;
|
|
@@ -57,14 +79,12 @@ export type IpcKit<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent
|
|
|
57
79
|
readonly streamBuffer: StreamBufferOptions;
|
|
58
80
|
};
|
|
59
81
|
readonly main: <R>(options: IpcMainOptions<C, R>) => IpcMainHandle<C>;
|
|
60
|
-
readonly preload: (options?: {
|
|
61
|
-
readonly global?: string;
|
|
62
|
-
}) => Promise<{
|
|
82
|
+
readonly preload: (options?: IpcPreloadOptions) => {
|
|
63
83
|
readonly global: string;
|
|
64
84
|
readonly bridge: IpcBridge;
|
|
65
85
|
readonly expose: () => void;
|
|
66
|
-
}
|
|
67
|
-
readonly renderer: (bridge: IpcBridge) => {
|
|
86
|
+
};
|
|
87
|
+
readonly renderer: (bridge: IpcBridge, options?: IpcRendererOptions) => {
|
|
68
88
|
readonly client: RpcClient<C>;
|
|
69
89
|
readonly events: EventSubscriber<C>;
|
|
70
90
|
readonly streamClient: StreamRpcClient<C>;
|
package/dist/kit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kit.d.ts","sourceRoot":"","sources":["../src/kit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EACV,QAAQ,EACR,SAAS,EACT,eAAe,EACf,WAAW,EACX,eAAe,EAChB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"kit.d.ts","sourceRoot":"","sources":["../src/kit.ts"],"names":[],"mappings":"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"}
|
package/dist/kit.js
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { createEventPublisher, createRpcEndpoint } from "./main.js";
|
|
3
|
+
import { createBridgeAdaptersFromBindings, exposeIpcBridgeFromBindings, resolveElectronRendererBindings, } from "./preload-bridge.js";
|
|
3
4
|
import { createEventSubscriber, createRpcClient, createStreamRpcClient } from "./renderer.js";
|
|
4
|
-
import { defaultChannelPrefix, } from "./types.js";
|
|
5
|
+
import { assertValidChannelPrefix, defaultChannelPrefix, } from "./types.js";
|
|
6
|
+
function loadElectronModule(electronModule) {
|
|
7
|
+
if (electronModule !== undefined) {
|
|
8
|
+
return electronModule;
|
|
9
|
+
}
|
|
10
|
+
if (typeof require === "function") {
|
|
11
|
+
return require("electron");
|
|
12
|
+
}
|
|
13
|
+
throw new Error("ipc.preload() could not load Electron synchronously. " +
|
|
14
|
+
"In ESM preload files, pass it explicitly: ipc.preload({ electronModule: electron }).");
|
|
15
|
+
}
|
|
5
16
|
export function createIpcKit(options) {
|
|
6
17
|
const normalizeStreamBuffer = (buffer) => {
|
|
7
18
|
if (!buffer || buffer.bufferSize === "unbounded") {
|
|
@@ -16,7 +27,7 @@ export function createIpcKit(options) {
|
|
|
16
27
|
};
|
|
17
28
|
const contract = options.contract;
|
|
18
29
|
const channelPrefix = options.channelPrefix
|
|
19
|
-
? { ...options.channelPrefix }
|
|
30
|
+
? assertValidChannelPrefix({ ...options.channelPrefix })
|
|
20
31
|
: { ...defaultChannelPrefix };
|
|
21
32
|
const bridgeGlobal = options.bridge?.global ?? "api";
|
|
22
33
|
const rpcDecodeMode = options.decode?.rpc ?? "envelope";
|
|
@@ -103,24 +114,24 @@ export function createIpcKit(options) {
|
|
|
103
114
|
stats: publisher.stats,
|
|
104
115
|
};
|
|
105
116
|
};
|
|
106
|
-
const preload =
|
|
107
|
-
const
|
|
117
|
+
const preload = (preloadOptions) => {
|
|
118
|
+
const bindings = resolveElectronRendererBindings(loadElectronModule(preloadOptions?.electronModule));
|
|
108
119
|
const global = preloadOptions?.global ?? bridgeGlobal;
|
|
109
|
-
const bridge =
|
|
120
|
+
const bridge = createBridgeAdaptersFromBindings(bindings, {
|
|
110
121
|
channelPrefix,
|
|
111
122
|
});
|
|
112
123
|
return {
|
|
113
124
|
global,
|
|
114
125
|
bridge,
|
|
115
126
|
expose: () => {
|
|
116
|
-
|
|
127
|
+
exposeIpcBridgeFromBindings(bindings, {
|
|
117
128
|
global,
|
|
118
129
|
channelPrefix,
|
|
119
130
|
});
|
|
120
131
|
},
|
|
121
132
|
};
|
|
122
133
|
};
|
|
123
|
-
const renderer = (bridge) => {
|
|
134
|
+
const renderer = (bridge, rendererOptions) => {
|
|
124
135
|
const hasStreamMethods = (contract.streamMethods?.length ?? 0) > 0;
|
|
125
136
|
if (hasStreamMethods && !bridge.onStreamFrame) {
|
|
126
137
|
throw new Error("Contract defines stream methods but bridge.onStreamFrame is missing. " +
|
|
@@ -132,6 +143,7 @@ export function createIpcKit(options) {
|
|
|
132
143
|
invoke: bridge.invoke,
|
|
133
144
|
onStreamFrame: bridge.onStreamFrame,
|
|
134
145
|
streamBuffer,
|
|
146
|
+
diagnostics: rendererOptions?.diagnostics?.stream,
|
|
135
147
|
});
|
|
136
148
|
}
|
|
137
149
|
const emptyStreamClient = Object.create(null);
|
|
@@ -139,10 +151,12 @@ export function createIpcKit(options) {
|
|
|
139
151
|
client: createRpcClient(contract, {
|
|
140
152
|
invoke: bridge.invoke,
|
|
141
153
|
rpcDecodeMode,
|
|
154
|
+
diagnostics: rendererOptions?.diagnostics?.rpc,
|
|
142
155
|
}),
|
|
143
156
|
events: createEventSubscriber(contract, {
|
|
144
157
|
subscribe: bridge.subscribe,
|
|
145
158
|
decodeMode: eventDecodeMode,
|
|
159
|
+
diagnostics: rendererOptions?.diagnostics?.events,
|
|
146
160
|
}),
|
|
147
161
|
streamClient: streamHandle?.client ?? emptyStreamClient,
|
|
148
162
|
dispose: () => {
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EAQZ,MAAM,eAAe,CAAC;AAcvB,OAAO,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EAQZ,MAAM,eAAe,CAAC;AAcvB,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,SAAS,EAEd,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EAIvB,MAAM,YAAY,CAAC;AAwCpB,wBAAgB,iBAAiB,CAC/B,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAC5C,KAAK,CAAC,aAAa,SAAS,aAAa,CAAC,eAAe,CAAC,GAAG,SAAS,EAAE,EACxE,CAAC,GAAG,KAAK,EAET,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EACrD,GAAG,EAAE,WAAW,EAChB,eAAe,EAAE,eAAe,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,EAChF,OAAO,EAAE,kBAAkB,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,GAC1E,WAAW,CAmeb;AAmBD,wBAAgB,oBAAoB,CAClC,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAC5C,KAAK,CAAC,aAAa,SAAS,aAAa,CAAC,eAAe,CAAC,GAAG,SAAS,EAAE,EAExE,SAAS,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EACtD,OAAO,EAAE,qBAAqB,GAC7B,iBAAiB,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAmNhE"}
|
package/dist/main.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import * as S from "
|
|
1
|
+
import * as S from "effect/Schema";
|
|
2
2
|
import { Cause, Effect, Exit, FiberId, Stream } from "effect";
|
|
3
3
|
import * as Runtime from "effect/Runtime";
|
|
4
4
|
import { isNoErrorSchema } from "./contract.js";
|
|
5
5
|
import { extractErrorTag, formatUnknown, isRecord, safelyCall, toDefectEnvelope, } from "./protocol.js";
|
|
6
|
-
import { defaultChannelPrefix, } from "./types.js";
|
|
6
|
+
import { assertValidChannelPrefix, defaultChannelPrefix, } from "./types.js";
|
|
7
7
|
function resolveChannelPrefix(prefix) {
|
|
8
|
-
return prefix
|
|
8
|
+
return prefix ? assertValidChannelPrefix(prefix) : defaultChannelPrefix;
|
|
9
9
|
}
|
|
10
10
|
function isWebContentsLike(value) {
|
|
11
11
|
if (!isRecord(value))
|
|
@@ -53,7 +53,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
53
53
|
const encodeSuccess = S.encodeSync(method.res);
|
|
54
54
|
const encodeFailure = isNoErrorSchema(method.err) ? null : S.encodeSync(method.err);
|
|
55
55
|
const channel = `${channelPrefix.rpc}${method.name}`;
|
|
56
|
-
listeners.set(channel, async function handleRpcRequest(
|
|
56
|
+
listeners.set(channel, async function handleRpcRequest(event, rawPayload) {
|
|
57
57
|
let input;
|
|
58
58
|
try {
|
|
59
59
|
input = decodeInput(rawPayload);
|
|
@@ -69,7 +69,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
69
69
|
}
|
|
70
70
|
let effect;
|
|
71
71
|
try {
|
|
72
|
-
effect = impl(input);
|
|
72
|
+
effect = impl(input, { sender: extractSender(event) });
|
|
73
73
|
}
|
|
74
74
|
catch (cause) {
|
|
75
75
|
return toDefectEnvelope(cause, `RPC ${method.name} implementation threw`);
|
|
@@ -121,6 +121,11 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
121
121
|
if (streamMethods.length > 0 && !streamHandlerImpls) {
|
|
122
122
|
throw new Error("Contract defines stream methods but no streamHandlers were provided.");
|
|
123
123
|
}
|
|
124
|
+
if (streamMethods.length === 0 &&
|
|
125
|
+
streamHandlerImpls &&
|
|
126
|
+
Object.keys(streamHandlerImpls).length > 0) {
|
|
127
|
+
throw new Error("streamHandlers were provided but the contract defines no stream methods.");
|
|
128
|
+
}
|
|
124
129
|
if (streamMethods.length > 0 && streamHandlerImpls) {
|
|
125
130
|
const streamMethodNames = new Set(streamMethods.map((m) => m.name));
|
|
126
131
|
for (const name in streamHandlerImpls) {
|
|
@@ -179,7 +184,7 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
179
184
|
}).pipe(Effect.ignore);
|
|
180
185
|
let handlerStream;
|
|
181
186
|
try {
|
|
182
|
-
handlerStream = impl(input);
|
|
187
|
+
handlerStream = impl(input, { sender });
|
|
183
188
|
}
|
|
184
189
|
catch (cause) {
|
|
185
190
|
return toDefectEnvelope(cause, `Stream ${method.name} implementation threw`);
|
|
@@ -198,11 +203,19 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
198
203
|
},
|
|
199
204
|
};
|
|
200
205
|
}
|
|
201
|
-
catch {
|
|
202
|
-
|
|
206
|
+
catch (encodeCause) {
|
|
207
|
+
return {
|
|
208
|
+
type: "defect",
|
|
209
|
+
streamId,
|
|
210
|
+
message: `Stream ${method.name} failure encoding failed: ${formatUnknown(encodeCause)}`,
|
|
211
|
+
};
|
|
203
212
|
}
|
|
204
213
|
}
|
|
205
|
-
return {
|
|
214
|
+
return {
|
|
215
|
+
type: "defect",
|
|
216
|
+
streamId,
|
|
217
|
+
message: `Stream ${method.name} returned a typed failure, but method declares NoError: ${formatUnknown(failure.value)}`,
|
|
218
|
+
};
|
|
206
219
|
}
|
|
207
220
|
if (Cause.isInterruptedOnly(cause)) {
|
|
208
221
|
return { type: "end", streamId };
|
|
@@ -214,23 +227,45 @@ export function createRpcEndpoint(contract, ipc, implementations, options) {
|
|
|
214
227
|
message: defect._tag === "Some" ? formatUnknown(defect.value) : "Stream failed unexpectedly",
|
|
215
228
|
};
|
|
216
229
|
};
|
|
217
|
-
const streamEffect = handlerStream.pipe(Stream.mapEffect((chunk) => Effect.try({
|
|
218
|
-
try: () => {
|
|
219
|
-
if (sender.isDestroyed())
|
|
220
|
-
return;
|
|
221
|
-
sender.send(sfChannel, { type: "data", streamId, payload: encodeChunk(chunk) });
|
|
222
|
-
},
|
|
223
|
-
catch: () => undefined,
|
|
224
|
-
}).pipe(Effect.ignore)), Stream.runDrain, Effect.andThen(() => trySend({ type: "end", streamId })), Effect.catchAllCause((cause) => sender.isDestroyed() ? Effect.void : trySend(buildTerminalFrame(cause))), Effect.ensuring(Effect.sync(() => {
|
|
225
|
-
activeStreams.delete(streamId);
|
|
226
|
-
})));
|
|
227
230
|
// Reserve entry BEFORE forking
|
|
228
231
|
const entry = {
|
|
229
232
|
fiber: null,
|
|
230
233
|
senderId: sender.id,
|
|
231
234
|
};
|
|
235
|
+
const onSenderDestroyed = () => {
|
|
236
|
+
entry.fiber?.unsafeInterruptAsFork(FiberId.none);
|
|
237
|
+
};
|
|
238
|
+
const streamEffect = handlerStream.pipe(Stream.mapEffect((chunk) => Effect.suspend(() => {
|
|
239
|
+
// A destroyed renderer can never consume more chunks:
|
|
240
|
+
// terminate the pipeline instead of draining the source.
|
|
241
|
+
if (sender.isDestroyed()) {
|
|
242
|
+
return Effect.interrupt;
|
|
243
|
+
}
|
|
244
|
+
return Effect.try({
|
|
245
|
+
try: () => sender.send(sfChannel, { type: "data", streamId, payload: encodeChunk(chunk) }),
|
|
246
|
+
catch: () => undefined,
|
|
247
|
+
}).pipe(Effect.ignore);
|
|
248
|
+
})), Stream.runDrain, Effect.andThen(() => trySend({ type: "end", streamId })), Effect.catchAllCause((cause) => sender.isDestroyed() ? Effect.void : trySend(buildTerminalFrame(cause))), Effect.ensuring(Effect.sync(() => {
|
|
249
|
+
activeStreams.delete(streamId);
|
|
250
|
+
if (typeof sender.removeListener === "function") {
|
|
251
|
+
sender.removeListener("destroyed", onSenderDestroyed);
|
|
252
|
+
}
|
|
253
|
+
})));
|
|
232
254
|
activeStreams.set(streamId, entry);
|
|
233
|
-
|
|
255
|
+
if (typeof sender.once === "function") {
|
|
256
|
+
sender.once("destroyed", onSenderDestroyed);
|
|
257
|
+
}
|
|
258
|
+
let fiber;
|
|
259
|
+
try {
|
|
260
|
+
fiber = runFork(streamEffect);
|
|
261
|
+
}
|
|
262
|
+
catch (cause) {
|
|
263
|
+
activeStreams.delete(streamId);
|
|
264
|
+
if (typeof sender.removeListener === "function") {
|
|
265
|
+
sender.removeListener("destroyed", onSenderDestroyed);
|
|
266
|
+
}
|
|
267
|
+
return toDefectEnvelope(cause, `Stream ${method.name} failed to start`);
|
|
268
|
+
}
|
|
234
269
|
entry.fiber = fiber;
|
|
235
270
|
const response = {
|
|
236
271
|
type: "success",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type ChannelPrefix, type EventSubscribe, type RpcInvoke } from "./types.ts";
|
|
2
|
+
export type BridgeAdapters = {
|
|
3
|
+
readonly invoke: RpcInvoke;
|
|
4
|
+
readonly subscribe: EventSubscribe;
|
|
5
|
+
readonly onStreamFrame: (listener: (frame: unknown) => void) => () => void;
|
|
6
|
+
};
|
|
7
|
+
export type BridgeAdaptersOptions = {
|
|
8
|
+
readonly channelPrefix?: ChannelPrefix;
|
|
9
|
+
};
|
|
10
|
+
export type BridgeExposureOptions = BridgeAdaptersOptions & {
|
|
11
|
+
readonly rpcGlobal?: string;
|
|
12
|
+
readonly eventsGlobal?: string;
|
|
13
|
+
};
|
|
14
|
+
export type IpcBridgeExposureOptions = BridgeAdaptersOptions & {
|
|
15
|
+
readonly global?: string;
|
|
16
|
+
};
|
|
17
|
+
type ContextBridgeLike = {
|
|
18
|
+
readonly exposeInMainWorld: (name: string, value: Record<string, unknown>) => void;
|
|
19
|
+
};
|
|
20
|
+
type IpcRendererLike = {
|
|
21
|
+
readonly invoke: (channel: string, payload: unknown) => Promise<unknown>;
|
|
22
|
+
readonly on: (channel: string, handler: (event: unknown, payload: unknown) => void) => void;
|
|
23
|
+
readonly removeListener: (channel: string, handler: (event: unknown, payload: unknown) => void) => void;
|
|
24
|
+
};
|
|
25
|
+
type ElectronRendererBindings = {
|
|
26
|
+
readonly contextBridge: ContextBridgeLike;
|
|
27
|
+
readonly ipcRenderer: IpcRendererLike;
|
|
28
|
+
};
|
|
29
|
+
export declare function resolveElectronRendererBindings(moduleCandidate: unknown): ElectronRendererBindings;
|
|
30
|
+
export declare function createBridgeAdaptersFromBindings(bindings: ElectronRendererBindings, options?: BridgeAdaptersOptions): BridgeAdapters;
|
|
31
|
+
export declare function exposeRpcBridgeFromBindings(bindings: ElectronRendererBindings, options?: BridgeExposureOptions): void;
|
|
32
|
+
export declare function exposeIpcBridgeFromBindings(bindings: ElectronRendererBindings, options?: IpcBridgeExposureOptions): void;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=preload-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preload-bridge.d.ts","sourceRoot":"","sources":["../src/preload-bridge.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,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;IACnC,QAAQ,CAAC,aAAa,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;CAC5E,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,qBAAqB,GAAG;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,qBAAqB,GAAG;IAC7D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACvB,QAAQ,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACpF,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE,QAAQ,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAC;IAC5F,QAAQ,CAAC,cAAc,EAAE,CACvB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,KAChD,IAAI,CAAC;CACX,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC1C,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;CACvC,CAAC;AAeF,wBAAgB,+BAA+B,CAC7C,eAAe,EAAE,OAAO,GACvB,wBAAwB,CAqB1B;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,wBAAwB,EAClC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAkChB;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,wBAAwB,EAClC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CAiBN;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,wBAAwB,EAClC,OAAO,CAAC,EAAE,wBAAwB,GACjC,IAAI,CAaN"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { isRecord } from "./protocol.js";
|
|
2
|
+
import { assertValidChannelPrefix, defaultChannelPrefix, } from "./types.js";
|
|
3
|
+
function isContextBridgeLike(value) {
|
|
4
|
+
return isRecord(value) && typeof value.exposeInMainWorld === "function";
|
|
5
|
+
}
|
|
6
|
+
function isIpcRendererLike(value) {
|
|
7
|
+
return (isRecord(value) &&
|
|
8
|
+
typeof value.invoke === "function" &&
|
|
9
|
+
typeof value.on === "function" &&
|
|
10
|
+
typeof value.removeListener === "function");
|
|
11
|
+
}
|
|
12
|
+
export function resolveElectronRendererBindings(moduleCandidate) {
|
|
13
|
+
const moduleDefault = isRecord(moduleCandidate) ? moduleCandidate.default : undefined;
|
|
14
|
+
const source = isRecord(moduleCandidate)
|
|
15
|
+
? moduleCandidate
|
|
16
|
+
: isRecord(moduleDefault)
|
|
17
|
+
? moduleDefault
|
|
18
|
+
: undefined;
|
|
19
|
+
if (!source) {
|
|
20
|
+
throw new Error("electron-effect-rpc/preload requires Electron preload runtime bindings.");
|
|
21
|
+
}
|
|
22
|
+
const { contextBridge, ipcRenderer } = source;
|
|
23
|
+
if (!isContextBridgeLike(contextBridge) || !isIpcRendererLike(ipcRenderer)) {
|
|
24
|
+
throw new Error("electron-effect-rpc/preload requires Electron preload runtime bindings.");
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
contextBridge,
|
|
28
|
+
ipcRenderer,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function createBridgeAdaptersFromBindings(bindings, options) {
|
|
32
|
+
const channelPrefix = options?.channelPrefix
|
|
33
|
+
? assertValidChannelPrefix(options.channelPrefix)
|
|
34
|
+
: defaultChannelPrefix;
|
|
35
|
+
const { ipcRenderer } = bindings;
|
|
36
|
+
const invoke = (method, payload) => ipcRenderer.invoke(`${channelPrefix.rpc}${method}`, payload);
|
|
37
|
+
const subscribe = (event, listener) => {
|
|
38
|
+
const wrapped = (_event, payload) => listener(payload);
|
|
39
|
+
const channel = `${channelPrefix.event}${event}`;
|
|
40
|
+
ipcRenderer.on(channel, wrapped);
|
|
41
|
+
return () => {
|
|
42
|
+
ipcRenderer.removeListener(channel, wrapped);
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
const onStreamFrame = (listener) => {
|
|
46
|
+
const channel = `${channelPrefix.rpc}sf`;
|
|
47
|
+
const wrapped = (_event, frame) => listener(frame);
|
|
48
|
+
ipcRenderer.on(channel, wrapped);
|
|
49
|
+
return () => {
|
|
50
|
+
ipcRenderer.removeListener(channel, wrapped);
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
invoke,
|
|
55
|
+
subscribe,
|
|
56
|
+
onStreamFrame,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function exposeRpcBridgeFromBindings(bindings, options) {
|
|
60
|
+
const rpcGlobal = options?.rpcGlobal ?? "rpc";
|
|
61
|
+
const eventsGlobal = options?.eventsGlobal ?? "events";
|
|
62
|
+
const { contextBridge } = bindings;
|
|
63
|
+
const adapters = createBridgeAdaptersFromBindings(bindings, {
|
|
64
|
+
channelPrefix: options?.channelPrefix,
|
|
65
|
+
});
|
|
66
|
+
contextBridge.exposeInMainWorld(rpcGlobal, {
|
|
67
|
+
invoke: adapters.invoke,
|
|
68
|
+
onStreamFrame: adapters.onStreamFrame,
|
|
69
|
+
});
|
|
70
|
+
contextBridge.exposeInMainWorld(eventsGlobal, {
|
|
71
|
+
subscribe: adapters.subscribe,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export function exposeIpcBridgeFromBindings(bindings, options) {
|
|
75
|
+
const global = options?.global ?? "api";
|
|
76
|
+
const { contextBridge } = bindings;
|
|
77
|
+
const adapters = createBridgeAdaptersFromBindings(bindings, {
|
|
78
|
+
channelPrefix: options?.channelPrefix,
|
|
79
|
+
});
|
|
80
|
+
contextBridge.exposeInMainWorld(global, {
|
|
81
|
+
invoke: adapters.invoke,
|
|
82
|
+
subscribe: adapters.subscribe,
|
|
83
|
+
onStreamFrame: adapters.onStreamFrame,
|
|
84
|
+
});
|
|
85
|
+
}
|
package/dist/preload.d.ts
CHANGED
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export type BridgeAdapters = {
|
|
3
|
-
readonly invoke: RpcInvoke;
|
|
4
|
-
readonly subscribe: EventSubscribe;
|
|
5
|
-
readonly onStreamFrame: (listener: (frame: unknown) => void) => () => void;
|
|
6
|
-
};
|
|
7
|
-
export type BridgeAdaptersOptions = {
|
|
8
|
-
readonly channelPrefix?: ChannelPrefix;
|
|
9
|
-
};
|
|
10
|
-
export type BridgeExposureOptions = BridgeAdaptersOptions & {
|
|
11
|
-
readonly rpcGlobal?: string;
|
|
12
|
-
readonly eventsGlobal?: string;
|
|
13
|
-
};
|
|
14
|
-
export type IpcBridgeExposureOptions = BridgeAdaptersOptions & {
|
|
15
|
-
readonly global?: string;
|
|
16
|
-
};
|
|
1
|
+
import { type BridgeAdapters, type BridgeAdaptersOptions, type BridgeExposureOptions, type IpcBridgeExposureOptions } from "./preload-bridge.ts";
|
|
17
2
|
export declare function createBridgeAdapters(options?: BridgeAdaptersOptions): BridgeAdapters;
|
|
18
3
|
export declare function exposeRpcBridge(options?: BridgeExposureOptions): void;
|
|
19
4
|
export declare function exposeIpcBridge(options?: IpcBridgeExposureOptions): void;
|
package/dist/preload.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preload.d.ts","sourceRoot":"","sources":["../src/preload.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"preload.d.ts","sourceRoot":"","sources":["../src/preload.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC9B,MAAM,qBAAqB,CAAC;AAM7B,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,cAAc,CAEpF;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAErE;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAExE"}
|
package/dist/preload.js
CHANGED
|
@@ -1,78 +1,14 @@
|
|
|
1
1
|
import * as electronModule from "electron";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
return isRecord(value) && typeof value.exposeInMainWorld === "function";
|
|
6
|
-
}
|
|
7
|
-
function isIpcRendererLike(value) {
|
|
8
|
-
return (isRecord(value) &&
|
|
9
|
-
typeof value.invoke === "function" &&
|
|
10
|
-
typeof value.on === "function" &&
|
|
11
|
-
typeof value.removeListener === "function");
|
|
12
|
-
}
|
|
13
|
-
function resolveElectronRendererBindings() {
|
|
14
|
-
const mod = electronModule;
|
|
15
|
-
const moduleDefault = isRecord(mod) ? mod.default : undefined;
|
|
16
|
-
const source = isRecord(moduleDefault) ? moduleDefault : isRecord(mod) ? mod : undefined;
|
|
17
|
-
if (!source) {
|
|
18
|
-
throw new Error("electron-effect-rpc/preload requires Electron preload runtime bindings.");
|
|
19
|
-
}
|
|
20
|
-
const { contextBridge, ipcRenderer } = source;
|
|
21
|
-
if (!isContextBridgeLike(contextBridge) || !isIpcRendererLike(ipcRenderer)) {
|
|
22
|
-
throw new Error("electron-effect-rpc/preload requires Electron preload runtime bindings.");
|
|
23
|
-
}
|
|
24
|
-
return { contextBridge, ipcRenderer };
|
|
2
|
+
import { createBridgeAdaptersFromBindings, exposeIpcBridgeFromBindings, exposeRpcBridgeFromBindings, resolveElectronRendererBindings, } from "./preload-bridge.js";
|
|
3
|
+
function resolveBindings() {
|
|
4
|
+
return resolveElectronRendererBindings(electronModule);
|
|
25
5
|
}
|
|
26
6
|
export function createBridgeAdapters(options) {
|
|
27
|
-
|
|
28
|
-
const { ipcRenderer } = resolveElectronRendererBindings();
|
|
29
|
-
const invoke = (method, payload) => ipcRenderer.invoke(`${channelPrefix.rpc}${method}`, payload);
|
|
30
|
-
const subscribe = (event, listener) => {
|
|
31
|
-
const wrapped = (_event, payload) => listener(payload);
|
|
32
|
-
const channel = `${channelPrefix.event}${event}`;
|
|
33
|
-
ipcRenderer.on(channel, wrapped);
|
|
34
|
-
return () => {
|
|
35
|
-
ipcRenderer.removeListener(channel, wrapped);
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
const onStreamFrame = (listener) => {
|
|
39
|
-
const channel = `${channelPrefix.rpc}sf`;
|
|
40
|
-
const wrapped = (_event, frame) => listener(frame);
|
|
41
|
-
ipcRenderer.on(channel, wrapped);
|
|
42
|
-
return () => {
|
|
43
|
-
ipcRenderer.removeListener(channel, wrapped);
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
return {
|
|
47
|
-
invoke,
|
|
48
|
-
subscribe,
|
|
49
|
-
onStreamFrame,
|
|
50
|
-
};
|
|
7
|
+
return createBridgeAdaptersFromBindings(resolveBindings(), options);
|
|
51
8
|
}
|
|
52
9
|
export function exposeRpcBridge(options) {
|
|
53
|
-
|
|
54
|
-
const eventsGlobal = options?.eventsGlobal ?? "events";
|
|
55
|
-
const { contextBridge } = resolveElectronRendererBindings();
|
|
56
|
-
const adapters = createBridgeAdapters({
|
|
57
|
-
channelPrefix: options?.channelPrefix,
|
|
58
|
-
});
|
|
59
|
-
contextBridge.exposeInMainWorld(rpcGlobal, {
|
|
60
|
-
invoke: adapters.invoke,
|
|
61
|
-
onStreamFrame: adapters.onStreamFrame,
|
|
62
|
-
});
|
|
63
|
-
contextBridge.exposeInMainWorld(eventsGlobal, {
|
|
64
|
-
subscribe: adapters.subscribe,
|
|
65
|
-
});
|
|
10
|
+
exposeRpcBridgeFromBindings(resolveBindings(), options);
|
|
66
11
|
}
|
|
67
12
|
export function exposeIpcBridge(options) {
|
|
68
|
-
|
|
69
|
-
const { contextBridge } = resolveElectronRendererBindings();
|
|
70
|
-
const adapters = createBridgeAdapters({
|
|
71
|
-
channelPrefix: options?.channelPrefix,
|
|
72
|
-
});
|
|
73
|
-
contextBridge.exposeInMainWorld(global, {
|
|
74
|
-
invoke: adapters.invoke,
|
|
75
|
-
subscribe: adapters.subscribe,
|
|
76
|
-
onStreamFrame: adapters.onStreamFrame,
|
|
77
|
-
});
|
|
13
|
+
exposeIpcBridgeFromBindings(resolveBindings(), options);
|
|
78
14
|
}
|
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,EAGL,KAAK,eAAe,EACpB,KAAK,WAAW,EAMjB,MAAM,eAAe,CAAC;AAUvB,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,SAAS,EAGd,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAE3B,KAAK,SAAS,EACd,KAAK,gBAAgB,EAMrB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC5B,MAAM,YAAY,CAAC;AAsEpB,wBAAgB,eAAe,CAC7B,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAC5C,KAAK,CAAC,aAAa,SAAS,aAAa,CAAC,eAAe,CAAC,GAAG,SAAS,EAAE,EAExE,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EACrD,OAAO,EAAE,gBAAgB,GACxB,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CA0JxD;AAqBD,wBAAgB,qBAAqB,CACnC,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAC5C,KAAK,CAAC,aAAa,SAAS,aAAa,CAAC,eAAe,CAAC,GAAG,SAAS,EAAE,EAExE,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EACrD,OAAO,EAAE,sBAAsB,GAC9B,eAAe,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,WAAW,EAMjB,MAAM,eAAe,CAAC;AAUvB,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,SAAS,EAGd,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAE3B,KAAK,SAAS,EACd,KAAK,gBAAgB,EAMrB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC5B,MAAM,YAAY,CAAC;AAsEpB,wBAAgB,eAAe,CAC7B,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAC5C,KAAK,CAAC,aAAa,SAAS,aAAa,CAAC,eAAe,CAAC,GAAG,SAAS,EAAE,EAExE,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EACrD,OAAO,EAAE,gBAAgB,GACxB,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CA0JxD;AAqBD,wBAAgB,qBAAqB,CACnC,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAC5C,KAAK,CAAC,aAAa,SAAS,aAAa,CAAC,eAAe,CAAC,GAAG,SAAS,EAAE,EAExE,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EACrD,OAAO,EAAE,sBAAsB,GAC9B,eAAe,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAmG9D;AAUD,wBAAgB,qBAAqB,CACnC,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAC5C,KAAK,CAAC,aAAa,SAAS,aAAa,CAAC,eAAe,CAAC,EAE1D,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EACrD,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CA2PpE;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/renderer.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as S from "
|
|
1
|
+
import * as S from "effect/Schema";
|
|
2
2
|
import { Cause, Effect, Exit, Stream } from "effect";
|
|
3
3
|
import { exitSchemaFor, isNoErrorSchema, } from "./contract.js";
|
|
4
4
|
import { extractStreamIdFromRaw, formatUnknown, isRecord, parseRpcResponseEnvelope, parseStreamFrame, safelyCall, } from "./protocol.js";
|
|
@@ -183,6 +183,9 @@ export function createEventSubscriber(contract, options) {
|
|
|
183
183
|
});
|
|
184
184
|
return registerUnsubscribe(unsubscribe);
|
|
185
185
|
};
|
|
186
|
+
const streamEvent = (event) => Stream.asyncPush((emit) => Effect.acquireRelease(Effect.sync(() => subscribeEvent(event, (payload) => {
|
|
187
|
+
emit.single(payload);
|
|
188
|
+
})), (unsubscribe) => Effect.sync(unsubscribe)));
|
|
186
189
|
const subscribeByName = (name, handler) => {
|
|
187
190
|
const event = eventMap.get(name);
|
|
188
191
|
if (!event) {
|
|
@@ -220,6 +223,7 @@ export function createEventSubscriber(contract, options) {
|
|
|
220
223
|
return {
|
|
221
224
|
subscribe: subscribeEvent,
|
|
222
225
|
subscribeByName,
|
|
226
|
+
stream: streamEvent,
|
|
223
227
|
dispose,
|
|
224
228
|
};
|
|
225
229
|
}
|
|
@@ -273,7 +277,7 @@ export function createStreamRpcClient(contract, options) {
|
|
|
273
277
|
frameDispatcher.delete(frame.streamId);
|
|
274
278
|
break;
|
|
275
279
|
case "defect":
|
|
276
|
-
handler.defect(frame.message);
|
|
280
|
+
handler.defect(frame.message, true);
|
|
277
281
|
frameDispatcher.delete(frame.streamId);
|
|
278
282
|
break;
|
|
279
283
|
}
|
|
@@ -289,6 +293,9 @@ export function createStreamRpcClient(contract, options) {
|
|
|
289
293
|
const payload = args.length === 0 ? {} : args[0];
|
|
290
294
|
return Stream.asyncPush((emit) => Effect.gen(function* () {
|
|
291
295
|
const streamId = crypto.randomUUID();
|
|
296
|
+
// Once main has sent a terminal frame (end/error/defect) there is
|
|
297
|
+
// nothing left to cancel; the finalizer skips the cancel invoke.
|
|
298
|
+
let serverTerminated = false;
|
|
292
299
|
// 1. Register in dispatch map BEFORE calling invoke
|
|
293
300
|
frameDispatcher.set(streamId, {
|
|
294
301
|
data: (rawPayload) => {
|
|
@@ -321,8 +328,12 @@ export function createStreamRpcClient(contract, options) {
|
|
|
321
328
|
});
|
|
322
329
|
}
|
|
323
330
|
},
|
|
324
|
-
end: () =>
|
|
331
|
+
end: () => {
|
|
332
|
+
serverTerminated = true;
|
|
333
|
+
emit.end();
|
|
334
|
+
},
|
|
325
335
|
error: (err) => {
|
|
336
|
+
serverTerminated = true;
|
|
326
337
|
if (!decodeTypedError) {
|
|
327
338
|
emit.fail(rpcDefect("stream_error_decode_failed", `Stream ${method.name} received typed error but declares NoError`, err));
|
|
328
339
|
return;
|
|
@@ -342,12 +353,19 @@ export function createStreamRpcClient(contract, options) {
|
|
|
342
353
|
emit.fail(rpcDefect("stream_error_decode_failed", `Stream ${method.name} error decode failed: ${formatUnknown(cause)}`, cause));
|
|
343
354
|
}
|
|
344
355
|
},
|
|
345
|
-
defect: (message) =>
|
|
356
|
+
defect: (message, fromServer = false) => {
|
|
357
|
+
if (fromServer) {
|
|
358
|
+
serverTerminated = true;
|
|
359
|
+
}
|
|
360
|
+
emit.fail(rpcDefect("remote_defect", message, undefined));
|
|
361
|
+
},
|
|
346
362
|
});
|
|
347
363
|
// 2. Register cleanup finalizer
|
|
348
364
|
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
349
365
|
frameDispatcher.delete(streamId);
|
|
350
|
-
}).pipe(Effect.andThen(Effect.
|
|
366
|
+
}).pipe(Effect.andThen(Effect.suspend(() => serverTerminated
|
|
367
|
+
? Effect.void
|
|
368
|
+
: Effect.tryPromise(() => invoke(`stream-cancel`, { streamId })).pipe(Effect.ignore)))));
|
|
351
369
|
// 3. Encode input
|
|
352
370
|
const encodedInput = yield* Effect.try({
|
|
353
371
|
try: () => encodeInput(payload),
|
package/dist/types.d.ts
CHANGED
|
@@ -3,18 +3,33 @@ import type * as Runtime from "effect/Runtime";
|
|
|
3
3
|
import type * as Stream from "effect/Stream";
|
|
4
4
|
import type { AnyEvent, AnyMethod, AnyStreamMethod, ExtractMethod, ExtractStreamMethod, RpcContract, RpcError, RpcEventPayload, RpcInput, RpcOutput, StreamChunk, StreamError, StreamInput } from "./contract.ts";
|
|
5
5
|
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
|
+
* Per-request context passed as the second argument to handlers.
|
|
8
|
+
* Handlers that don't need it can simply omit the parameter.
|
|
9
|
+
*/
|
|
10
|
+
export type RpcHandlerContext = {
|
|
11
|
+
/** The webContents that sent the request, when the transport exposes it. */
|
|
12
|
+
readonly sender: WebContentsLike | null;
|
|
13
|
+
};
|
|
6
14
|
export type Implementations<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[], readonly AnyStreamMethod[]>, R = never> = {
|
|
7
|
-
readonly [Name in C["methods"][number]["name"]]: (input: RpcInput<ExtractMethod<C["methods"], Name
|
|
15
|
+
readonly [Name in C["methods"][number]["name"]]: (input: RpcInput<ExtractMethod<C["methods"], Name>>, context: RpcHandlerContext) => Effect.Effect<RpcOutput<ExtractMethod<C["methods"], Name>>, RpcError<ExtractMethod<C["methods"], Name>>, R>;
|
|
8
16
|
};
|
|
9
17
|
export type StreamImplementations<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[], readonly AnyStreamMethod[]>, R = never> = {
|
|
10
|
-
readonly [Name in C["streamMethods"][number]["name"]]: (input: StreamInput<ExtractStreamMethod<C["streamMethods"], Name
|
|
18
|
+
readonly [Name in C["streamMethods"][number]["name"]]: (input: StreamInput<ExtractStreamMethod<C["streamMethods"], Name>>, context: RpcHandlerContext) => Stream.Stream<StreamChunk<ExtractStreamMethod<C["streamMethods"], Name>>, StreamError<ExtractStreamMethod<C["streamMethods"], Name>>, R>;
|
|
11
19
|
};
|
|
12
20
|
export type WebContentsLike = {
|
|
13
21
|
readonly id: number;
|
|
14
22
|
readonly isDestroyed: () => boolean;
|
|
15
23
|
readonly send: (channel: string, payload: unknown) => void;
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Optional EventEmitter surface (present on real Electron WebContents).
|
|
26
|
+
* When available, active stream fibers are interrupted as soon as the
|
|
27
|
+
* renderer is destroyed; otherwise termination happens on the next chunk.
|
|
28
|
+
*/
|
|
29
|
+
readonly once?: (event: "destroyed", listener: () => void) => unknown;
|
|
30
|
+
readonly removeListener?: (event: "destroyed", listener: () => void) => unknown;
|
|
31
|
+
};
|
|
32
|
+
type IsEmptyObject<T> = T extends object ? keyof T extends never ? string extends T ? true : false : false : false;
|
|
18
33
|
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";
|
|
19
34
|
export declare class RpcDefectError extends Error {
|
|
20
35
|
readonly code: RpcDefectCode;
|
|
@@ -37,6 +52,12 @@ export type ChannelPrefix = {
|
|
|
37
52
|
readonly event: string;
|
|
38
53
|
};
|
|
39
54
|
export declare const defaultChannelPrefix: ChannelPrefix;
|
|
55
|
+
/**
|
|
56
|
+
* RPC and event prefixes must differ: with identical prefixes an event named
|
|
57
|
+
* like a method (or named "sf"/"stream-cancel") would collide with RPC and
|
|
58
|
+
* stream transport channels.
|
|
59
|
+
*/
|
|
60
|
+
export declare function assertValidChannelPrefix(prefix: ChannelPrefix): ChannelPrefix;
|
|
40
61
|
export type DecodeFailureScope = "rpc-request" | "rpc-response" | "event-payload" | "stream-request" | "stream-chunk" | "stream-error";
|
|
41
62
|
export type DecodeFailureContext = {
|
|
42
63
|
readonly scope: DecodeFailureScope;
|
|
@@ -111,11 +132,21 @@ export type EventPublisherOptions = {
|
|
|
111
132
|
readonly diagnostics?: EventPublisherDiagnostics;
|
|
112
133
|
};
|
|
113
134
|
export interface RpcEventPublisher<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[], readonly AnyStreamMethod[]>> {
|
|
135
|
+
/**
|
|
136
|
+
* Enqueue an event for delivery. Never fails: after `dispose()` the event
|
|
137
|
+
* is silently discarded, and delivery problems surface only through the
|
|
138
|
+
* diagnostics hooks and `stats()`.
|
|
139
|
+
*/
|
|
114
140
|
readonly publish: <E extends C["events"][number]>(event: E, payload: RpcEventPayload<E>) => Effect.Effect<void, never>;
|
|
115
141
|
readonly start: () => void;
|
|
116
142
|
readonly stop: () => void;
|
|
117
143
|
readonly dispose: () => void;
|
|
118
144
|
readonly isRunning: () => boolean;
|
|
145
|
+
/**
|
|
146
|
+
* `dropped` counts failed deliveries, not whole events: an event that
|
|
147
|
+
* reaches two of three windows increments it once (per failed window),
|
|
148
|
+
* as do queue evictions and encoding failures.
|
|
149
|
+
*/
|
|
119
150
|
readonly stats: () => {
|
|
120
151
|
readonly queued: number;
|
|
121
152
|
readonly dropped: number;
|
|
@@ -134,6 +165,12 @@ export type EventSubscriberOptions = {
|
|
|
134
165
|
export interface EventSubscriber<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[], readonly AnyStreamMethod[]>> {
|
|
135
166
|
readonly subscribe: <E extends C["events"][number]>(event: E, handler: (payload: RpcEventPayload<E>) => void) => () => void;
|
|
136
167
|
readonly subscribeByName: (name: string, handler: (payload: unknown) => void) => () => void;
|
|
168
|
+
/**
|
|
169
|
+
* Effect-native subscription: a Stream of decoded payloads that
|
|
170
|
+
* subscribes when run and unsubscribes when its scope closes.
|
|
171
|
+
* Payloads that fail to decode are skipped (reported via diagnostics).
|
|
172
|
+
*/
|
|
173
|
+
readonly stream: <E extends C["events"][number]>(event: E) => Stream.Stream<RpcEventPayload<E>>;
|
|
137
174
|
readonly dispose: () => void;
|
|
138
175
|
}
|
|
139
176
|
export type OnStreamFrame = (listener: (frame: unknown) => void) => () => void;
|
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,KAAK,MAAM,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EACV,QAAQ,EACR,SAAS,EACT,eAAe,EAEf,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,QAAQ,EAER,eAAe,EACf,QAAQ,EAER,SAAS,EAET,WAAW,EACX,WAAW,EACX,WAAW,EAEZ,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,QAAQ,EACR,SAAS,EACT,eAAe,EACf,WAAW,EACX,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,QAAQ,EACR,SAAS,EACT,SAAS,EACT,eAAe,EACf,WAAW,EACX,WAAW,EACX,WAAW,EACX,eAAe,GAChB,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,eAAe,CACzB,CAAC,SAAS,WAAW,CAAC,SAAS,SAAS,EAAE,EAAE,SAAS,QAAQ,EAAE,EAAE,SAAS,eAAe,EAAE,CAAC,EAC5F,CAAC,GAAG,KAAK,IACP;IACF,QAAQ,EAAE,IAAI,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAC/C,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -11,3 +11,14 @@ export const defaultChannelPrefix = {
|
|
|
11
11
|
rpc: "rpc/",
|
|
12
12
|
event: "event/",
|
|
13
13
|
};
|
|
14
|
+
/**
|
|
15
|
+
* RPC and event prefixes must differ: with identical prefixes an event named
|
|
16
|
+
* like a method (or named "sf"/"stream-cancel") would collide with RPC and
|
|
17
|
+
* stream transport channels.
|
|
18
|
+
*/
|
|
19
|
+
export function assertValidChannelPrefix(prefix) {
|
|
20
|
+
if (prefix.rpc === prefix.event) {
|
|
21
|
+
throw new Error(`channelPrefix.rpc and channelPrefix.event must differ (both were "${prefix.rpc}").`);
|
|
22
|
+
}
|
|
23
|
+
return prefix;
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electron-effect-rpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Typed IPC RPC for Electron, built on Effect and @effect/schema",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"effect",
|
|
@@ -13,12 +13,14 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
16
|
-
"url": "https://github.com/joaoeira/electron-effect-rpc"
|
|
16
|
+
"url": "git+https://github.com/joaoeira/electron-effect-rpc.git"
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
|
-
"dist"
|
|
19
|
+
"dist",
|
|
20
|
+
"CHANGELOG.md"
|
|
20
21
|
],
|
|
21
22
|
"type": "module",
|
|
23
|
+
"sideEffects": false,
|
|
22
24
|
"exports": {
|
|
23
25
|
".": {
|
|
24
26
|
"types": "./dist/index.d.ts",
|
|
@@ -66,7 +68,6 @@
|
|
|
66
68
|
"fmt:check": "oxfmt --check ."
|
|
67
69
|
},
|
|
68
70
|
"devDependencies": {
|
|
69
|
-
"@effect/schema": "^0.69.0",
|
|
70
71
|
"@types/node": "^22.0.0",
|
|
71
72
|
"bun-types": "^1.1.0",
|
|
72
73
|
"effect": "^3.11.0",
|
|
@@ -76,8 +77,10 @@
|
|
|
76
77
|
"typescript": "^5.7.0"
|
|
77
78
|
},
|
|
78
79
|
"peerDependencies": {
|
|
79
|
-
"
|
|
80
|
-
"effect": ">=3.0.0",
|
|
80
|
+
"effect": ">=3.10.0",
|
|
81
81
|
"electron": ">=28.0.0"
|
|
82
|
+
},
|
|
83
|
+
"engines": {
|
|
84
|
+
"node": ">=18.0.0"
|
|
82
85
|
}
|
|
83
86
|
}
|