electron-effect-rpc 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +90 -0
- package/README.md +145 -71
- package/dist/boundary.d.ts +17 -0
- package/dist/boundary.d.ts.map +1 -0
- package/dist/boundary.js +89 -0
- package/dist/contract.d.ts +7 -9
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +9 -10
- package/dist/kit.d.ts +25 -9
- package/dist/kit.d.ts.map +1 -1
- package/dist/kit.js +15 -7
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +109 -68
- package/dist/preload-bridge.d.ts +36 -8
- package/dist/preload-bridge.d.ts.map +1 -1
- package/dist/preload-bridge.js +26 -22
- package/dist/protocol.d.ts +12 -12
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +35 -53
- package/dist/renderer.d.ts.map +1 -1
- package/dist/renderer.js +177 -107
- package/dist/testing.d.ts +4 -3
- package/dist/testing.d.ts.map +1 -1
- package/dist/types.d.ts +71 -27
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +11 -0
- package/package.json +13 -9
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.10.0 - 2026-08-15
|
|
6
|
+
|
|
7
|
+
### Breaking
|
|
8
|
+
|
|
9
|
+
- The package now requires Effect v4 (`effect@^4.0.0-rc.109`). Effect v3 is no
|
|
10
|
+
longer supported.
|
|
11
|
+
- `RpcEndpointOptions.runtime` and `ipc.main({ runtime })` were replaced by
|
|
12
|
+
`context`, which accepts a v4 `Context.Context<R>`. Use `Context.empty()` when
|
|
13
|
+
handlers require no application services.
|
|
14
|
+
- Renderer streams now use Effect v4's `Stream.callback` and `Queue` APIs.
|
|
15
|
+
- Cause extraction, fiber interruption, legacy `Exit` codecs, and schema
|
|
16
|
+
constraints were migrated to their Effect v4 equivalents.
|
|
17
|
+
- `event()` no longer accepts a third `context` schema argument. It was never
|
|
18
|
+
read by the publisher or subscriber; remove it from any `event(...)` calls.
|
|
19
|
+
- `channelPrefix.rpc` and `channelPrefix.event` must now differ; identical
|
|
20
|
+
prefixes throw at construction time.
|
|
21
|
+
- Providing `streamHandlers` when the contract defines no `streamMethods` now
|
|
22
|
+
throws instead of being silently ignored.
|
|
23
|
+
- Methods whose request schema type is `object` (e.g. `S.ObjectKeyword`) now require
|
|
24
|
+
an argument at the call site; previously the client generated a zero-arg
|
|
25
|
+
caller that always sent `{}`.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Handlers (unary and stream) receive an optional second `context` argument:
|
|
30
|
+
`{ sender: WebContentsLike | null }`.
|
|
31
|
+
- `EventSubscriber.stream(event)` — Effect-native event consumption as a
|
|
32
|
+
`Stream` that unsubscribes when its scope closes.
|
|
33
|
+
- `ipc.renderer(bridge, { diagnostics })` — renderer-side diagnostics hooks for
|
|
34
|
+
RPC, events, and streams in kit mode.
|
|
35
|
+
- `assertValidChannelPrefix` exported from `electron-effect-rpc/types`.
|
|
36
|
+
- Named structured-clone transport types (`IpcEncodedValue`,
|
|
37
|
+
`IpcEncodedRecord`, and `DiagnosticCause`) exported from
|
|
38
|
+
`electron-effect-rpc/types`.
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Main-side stream fibers no longer leak when the renderer goes away without
|
|
43
|
+
cancelling: a destroyed sender now terminates the pipeline on the next chunk,
|
|
44
|
+
and when the real Electron `WebContents` emitter is available the fiber is
|
|
45
|
+
interrupted immediately on `destroyed`.
|
|
46
|
+
- Normally-completed streams no longer send a redundant `stream-cancel` invoke.
|
|
47
|
+
- `isNoErrorSchema` now matches `Never` schemas by AST tag, so a duplicated
|
|
48
|
+
schema package instance can't silently turn typed failures into defects.
|
|
49
|
+
- Stream failure-encoding defects now carry the encoding error message instead
|
|
50
|
+
of `[object Object]`.
|
|
51
|
+
- Preload bindings now accept the real Electron module without casts, preserve
|
|
52
|
+
Electron method receivers, and report a descriptive error when synchronous
|
|
53
|
+
loading is unavailable in ESM.
|
|
54
|
+
- Protocol envelopes and stream frames preserve structured-clone payloads such
|
|
55
|
+
as `bigint`, maps, sets, binary values, and cyclic object graphs while leaving
|
|
56
|
+
domain validation to each contract schema.
|
|
57
|
+
|
|
58
|
+
## 0.8.0 and earlier
|
|
59
|
+
|
|
60
|
+
`getWindow` was renamed to `getWindows` and now returns an array, enabling
|
|
61
|
+
multi-window event fan-out. Empty array replaces `null` for "no windows."
|
|
62
|
+
|
|
63
|
+
Before:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
getWindow: () => mainWindow,
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
After:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
getWindows: () => [mainWindow],
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Renderer RPC methods now return `Effect.Effect` instead of `Promise`, and
|
|
76
|
+
`IpcMainHandle.emit` was removed in favor of `publish`.
|
|
77
|
+
|
|
78
|
+
Before:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const result = await client.GetAppVersion();
|
|
82
|
+
await mainRpc.emit(WorkUnitProgress, payload);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
After:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
const result = await Effect.runPromise(client.GetAppVersion());
|
|
89
|
+
await Effect.runPromise(mainRpc.publish(WorkUnitProgress, payload));
|
|
90
|
+
```
|
package/README.md
CHANGED
|
@@ -1,35 +1,43 @@
|
|
|
1
1
|
# electron-effect-rpc
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/electron-effect-rpc)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Typed, schema-validated IPC for Electron, built on [Effect](https://effect.website).
|
|
6
|
+
|
|
7
|
+
Define your IPC surface once — methods, events, and streams, each described
|
|
8
|
+
with `effect/Schema` — and get a fully typed client in the renderer, fully
|
|
9
|
+
typed handlers in main, and runtime validation at every process boundary. No
|
|
10
|
+
hand-rolled channel strings, no `any`-typed `invoke` calls, no drift between
|
|
11
|
+
processes.
|
|
12
|
+
|
|
13
|
+
The primary API is a single shared `createIpcKit` configuration reused across
|
|
14
|
+
main, preload, and renderer. Low-level per-piece factories remain available as
|
|
15
|
+
subpath imports.
|
|
8
16
|
|
|
9
17
|
This package is ESM-only. It targets modern Electron runtimes and assumes an
|
|
10
18
|
ESM-capable build pipeline.
|
|
11
19
|
|
|
12
20
|
## Features
|
|
13
21
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- Effect
|
|
18
|
-
- Streaming RPC: handlers return `Stream.Stream`, clients consume `Stream.Stream
|
|
19
|
-
-
|
|
20
|
-
- Explicit lifecycle handles and bounded event queue backpressure.
|
|
22
|
+
- One contract for methods, events, and streaming RPC, shared by all three processes.
|
|
23
|
+
- One kit config, so channel naming can never drift between processes.
|
|
24
|
+
- Schema validation on every IPC crossing, in both directions.
|
|
25
|
+
- Typed domain errors in the Effect error channel; transport problems as `RpcDefectError` with stable codes.
|
|
26
|
+
- Streaming RPC: handlers return `Stream.Stream`, clients consume `Stream.Stream`, cancellation propagates.
|
|
27
|
+
- Main handlers are Effects, run with an injected `Context` (so your services are available).
|
|
28
|
+
- Explicit lifecycle handles (`start`/`stop`/`dispose`) and bounded event queue backpressure.
|
|
21
29
|
- Structured diagnostics hooks for decode/protocol/dispatch failures.
|
|
22
30
|
|
|
23
31
|
## Requirements
|
|
24
32
|
|
|
25
|
-
- Electron with context isolation enabled.
|
|
33
|
+
- Electron >= 28 with context isolation enabled.
|
|
26
34
|
- ESM-capable bundling.
|
|
27
|
-
- Peer dependencies: `effect
|
|
35
|
+
- Peer dependencies: `effect` (`^4.0.0-rc.109`), `electron`.
|
|
28
36
|
|
|
29
37
|
## Installation
|
|
30
38
|
|
|
31
39
|
```sh
|
|
32
|
-
bun add electron-effect-rpc effect
|
|
40
|
+
bun add electron-effect-rpc effect
|
|
33
41
|
```
|
|
34
42
|
|
|
35
43
|
## Quickstart (Kit-First)
|
|
@@ -37,7 +45,7 @@ bun add electron-effect-rpc effect @effect/schema
|
|
|
37
45
|
### 1) Define contract and kit once
|
|
38
46
|
|
|
39
47
|
```ts
|
|
40
|
-
import * as S from "
|
|
48
|
+
import * as S from "effect/Schema";
|
|
41
49
|
import { createIpcKit, defineContract, event, rpc, streamRpc } from "electron-effect-rpc";
|
|
42
50
|
|
|
43
51
|
export const GetAppVersion = rpc("GetAppVersion", S.Struct({}), S.Struct({ version: S.String }));
|
|
@@ -63,36 +71,51 @@ const contract = defineContract({
|
|
|
63
71
|
streamMethods: [StreamAiGeneration] as const,
|
|
64
72
|
});
|
|
65
73
|
|
|
74
|
+
export const ipc = createIpcKit({ contract });
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`createIpcKit` accepts optional configuration; the values below are the
|
|
78
|
+
defaults, so only set them to deviate:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
66
81
|
export const ipc = createIpcKit({
|
|
67
82
|
contract,
|
|
68
83
|
channelPrefix: { rpc: "rpc/", event: "event/" },
|
|
69
84
|
bridge: { global: "api" },
|
|
70
85
|
decode: { rpc: "envelope", events: "safe" },
|
|
86
|
+
streamBuffer: { bufferSize: "unbounded" },
|
|
71
87
|
});
|
|
72
88
|
```
|
|
73
89
|
|
|
74
90
|
### 2) Main process
|
|
75
91
|
|
|
76
92
|
```ts
|
|
77
|
-
import
|
|
78
|
-
import {
|
|
79
|
-
import
|
|
93
|
+
import path from "node:path";
|
|
94
|
+
import { app, BrowserWindow, ipcMain } from "electron";
|
|
95
|
+
import { Context, Effect, Stream } from "effect";
|
|
80
96
|
import { ipc, WorkUnitProgress } from "./shared-ipc.ts";
|
|
81
97
|
|
|
98
|
+
const mainWindow = new BrowserWindow({
|
|
99
|
+
webPreferences: { preload: path.join(import.meta.dirname, "preload.js") },
|
|
100
|
+
});
|
|
101
|
+
|
|
82
102
|
const mainRpc = ipc.main({
|
|
83
103
|
ipcMain,
|
|
84
104
|
handlers: {
|
|
85
105
|
GetAppVersion: () => Effect.succeed({ version: app.getVersion() }),
|
|
106
|
+
// Handlers may take an optional second argument with request context:
|
|
107
|
+
// GetAppVersion: (_input, { sender }) => ...
|
|
86
108
|
},
|
|
87
109
|
streamHandlers: {
|
|
88
110
|
StreamAiGeneration: ({ prompt }) =>
|
|
89
111
|
Stream.fromIterable(prompt.split(" ")).pipe(Stream.map((word) => ({ delta: word + " " }))),
|
|
90
112
|
},
|
|
91
|
-
|
|
113
|
+
context: Context.empty(),
|
|
92
114
|
getWindows: () => [mainWindow],
|
|
93
115
|
});
|
|
94
116
|
|
|
95
117
|
mainRpc.start();
|
|
118
|
+
app.on("will-quit", () => mainRpc.dispose());
|
|
96
119
|
|
|
97
120
|
void Effect.runPromise(
|
|
98
121
|
mainRpc.publish(WorkUnitProgress, {
|
|
@@ -145,82 +168,132 @@ const unsubscribe = events.subscribe(WorkUnitProgress, (payload) => {
|
|
|
145
168
|
console.log(payload.chunk);
|
|
146
169
|
});
|
|
147
170
|
|
|
171
|
+
// Or consume events as an Effect Stream (unsubscribes when the scope closes):
|
|
172
|
+
await Effect.runPromise(
|
|
173
|
+
events
|
|
174
|
+
.stream(WorkUnitProgress)
|
|
175
|
+
.pipe(Stream.runForEach((payload) => Effect.sync(() => console.log(payload.chunk)))),
|
|
176
|
+
);
|
|
177
|
+
|
|
148
178
|
// later
|
|
149
179
|
unsubscribe();
|
|
150
180
|
dispose();
|
|
151
181
|
```
|
|
152
182
|
|
|
153
|
-
|
|
183
|
+
Renderer-side diagnostics hooks are available through the second argument:
|
|
154
184
|
|
|
155
185
|
```ts
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
}
|
|
186
|
+
const renderer = ipc.renderer(window.api, {
|
|
187
|
+
diagnostics: {
|
|
188
|
+
rpc: { onDecodeFailure: (ctx) => console.warn("rpc decode failure", ctx) },
|
|
189
|
+
events: { onDecodeFailure: (ctx) => console.warn("event decode failure", ctx) },
|
|
190
|
+
},
|
|
191
|
+
});
|
|
165
192
|
```
|
|
166
193
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
Domain failures are modeled with tagged error schemas and are surfaced in the
|
|
170
|
-
Effect error channel as those same tagged values. Unexpected failures,
|
|
171
|
-
transport defects, and protocol mismatches are surfaced as `RpcDefectError`,
|
|
172
|
-
which includes a stable `code` discriminator:
|
|
173
|
-
`request_encoding_failed`, `invoke_failed`,
|
|
174
|
-
`success_payload_decoding_failed`, `failure_payload_decoding_failed`,
|
|
175
|
-
`noerror_contract_violation`, `invalid_response_envelope`,
|
|
176
|
-
`legacy_decode_failed`, `remote_defect`,
|
|
177
|
-
`stream_invoke_failed`, `stream_handshake_invalid`,
|
|
178
|
-
`stream_chunk_decode_failed`, and `stream_error_decode_failed`.
|
|
179
|
-
|
|
180
|
-
## Breaking Changes
|
|
181
|
-
|
|
182
|
-
`getWindow` was renamed to `getWindows` and now returns an array, enabling
|
|
183
|
-
multi-window event fan-out. Empty array replaces `null` for "no windows."
|
|
194
|
+
### 5) Window typing
|
|
184
195
|
|
|
185
|
-
|
|
196
|
+
Use the exported `IpcBridgeGlobal` helper so the declared shape stays in sync
|
|
197
|
+
with what `ipc.preload()` exposes:
|
|
186
198
|
|
|
187
199
|
```ts
|
|
188
|
-
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
After:
|
|
200
|
+
import type { IpcBridgeGlobal } from "electron-effect-rpc";
|
|
192
201
|
|
|
193
|
-
|
|
194
|
-
|
|
202
|
+
declare global {
|
|
203
|
+
interface Window extends IpcBridgeGlobal {}
|
|
204
|
+
}
|
|
195
205
|
```
|
|
196
206
|
|
|
197
|
-
|
|
198
|
-
`IpcMainHandle.emit` was removed in favor of `publish`.
|
|
207
|
+
For a custom global name, use `IpcBridgeGlobal<"myBridge">`.
|
|
199
208
|
|
|
200
|
-
|
|
209
|
+
## Error Model
|
|
201
210
|
|
|
202
|
-
|
|
203
|
-
const result = await client.GetAppVersion();
|
|
204
|
-
await mainRpc.emit(WorkUnitProgress, payload);
|
|
205
|
-
```
|
|
211
|
+
A call site sees exactly two kinds of failure in the Effect error channel:
|
|
206
212
|
|
|
207
|
-
|
|
213
|
+
- **Domain failures** — the tagged errors you declared in the contract's error
|
|
214
|
+
schema, decoded back into those same tagged values.
|
|
215
|
+
- **`RpcDefectError`** — everything else: transport problems, schema
|
|
216
|
+
mismatches, and unexpected main-process exceptions. It is itself tagged
|
|
217
|
+
(`_tag: "RpcDefectError"`) and carries a stable `code` discriminator.
|
|
208
218
|
|
|
209
219
|
```ts
|
|
210
|
-
|
|
211
|
-
|
|
220
|
+
import { Effect } from "effect";
|
|
221
|
+
|
|
222
|
+
const result = await Effect.runPromise(
|
|
223
|
+
client.DeleteWorkspace({ id }).pipe(
|
|
224
|
+
// a tagged error you declared in the contract
|
|
225
|
+
Effect.catchTag("AccessDeniedError", (e) =>
|
|
226
|
+
Effect.succeed({ deleted: false, reason: e.message }),
|
|
227
|
+
),
|
|
228
|
+
// transport/contract problems
|
|
229
|
+
Effect.catchTag("RpcDefectError", (defect) =>
|
|
230
|
+
Effect.sync(() => log.error(defect.code, defect.message)).pipe(
|
|
231
|
+
Effect.andThen(Effect.fail(defect)),
|
|
232
|
+
),
|
|
233
|
+
),
|
|
234
|
+
),
|
|
235
|
+
);
|
|
212
236
|
```
|
|
213
237
|
|
|
238
|
+
`RpcDefectError.code` values:
|
|
239
|
+
|
|
240
|
+
| Code | Meaning |
|
|
241
|
+
| --------------------------------- | ------------------------------------------------------------------- |
|
|
242
|
+
| `request_encoding_failed` | Request payload failed schema encoding before leaving the renderer. |
|
|
243
|
+
| `invoke_failed` | The underlying `ipcRenderer.invoke` rejected (transport failure). |
|
|
244
|
+
| `success_payload_decoding_failed` | Main's success payload failed response schema decoding. |
|
|
245
|
+
| `failure_payload_decoding_failed` | Main's typed failure payload failed error schema decoding. |
|
|
246
|
+
| `noerror_contract_violation` | A failure arrived for a method that declares `NoError`. |
|
|
247
|
+
| `invalid_response_envelope` | The response was not a valid envelope. |
|
|
248
|
+
| `legacy_decode_failed` | `dual` decode mode could not parse a legacy `Exit` payload. |
|
|
249
|
+
| `remote_defect` | The main-side handler died, threw, or was interrupted. |
|
|
250
|
+
| `stream_invoke_failed` | The stream handshake invoke rejected (transport failure). |
|
|
251
|
+
| `stream_handshake_invalid` | The stream handshake response had an unexpected shape. |
|
|
252
|
+
| `stream_chunk_decode_failed` | A stream chunk failed schema decoding. |
|
|
253
|
+
| `stream_error_decode_failed` | A stream's typed error frame failed schema decoding. |
|
|
254
|
+
|
|
255
|
+
Defect envelopes carry the main-process error message verbatim across IPC. If
|
|
256
|
+
any window in your app loads remote or less-trusted content, treat that as
|
|
257
|
+
information disclosure: catch and sanitize errors in your handlers rather than
|
|
258
|
+
letting raw exceptions (paths, query fragments) become defects.
|
|
259
|
+
|
|
260
|
+
## Operational Notes
|
|
261
|
+
|
|
262
|
+
**Stream backpressure.** Stream frames are pushed from main as fast as the
|
|
263
|
+
handler produces them; there is no acknowledgment across the IPC boundary. The
|
|
264
|
+
renderer buffers with `bufferSize: "unbounded"` by default, which is lossless
|
|
265
|
+
but means a fast producer with a slow consumer grows renderer memory. Bounded
|
|
266
|
+
buffers (`dropping`/`sliding`) are lossy by design, while completion and failure
|
|
267
|
+
signals remain reliable through Effect v4's `Stream.callback` queue. Rate-limit
|
|
268
|
+
fast producers in the handler (`Stream.throttle`, batching) rather than relying
|
|
269
|
+
on a bounded renderer buffer.
|
|
270
|
+
|
|
271
|
+
**Unary RPC cancellation.** Interrupting the renderer-side Effect of a
|
|
272
|
+
client call (e.g. via `Effect.timeout`) does not abort the main-side handler;
|
|
273
|
+
the underlying `ipcRenderer.invoke` is not abortable. The handler runs to
|
|
274
|
+
completion and its response is discarded. Streaming RPC does propagate
|
|
275
|
+
cancellation to main. If a unary handler does expensive work, model it as a
|
|
276
|
+
stream or build explicit cancellation into your contract.
|
|
277
|
+
|
|
278
|
+
**Renderer teardown.** Main interrupts a stream's fiber when the renderer's
|
|
279
|
+
`webContents` is destroyed (immediately when the real Electron `WebContents`
|
|
280
|
+
event emitter is available, otherwise on the next chunk), so handler fibers do
|
|
281
|
+
not outlive closed windows.
|
|
282
|
+
|
|
283
|
+
## Migration Notes
|
|
284
|
+
|
|
285
|
+
See [CHANGELOG.md](./CHANGELOG.md) for breaking changes between versions.
|
|
286
|
+
|
|
214
287
|
## Low-Level APIs (Still Supported)
|
|
215
288
|
|
|
216
289
|
If you need direct control, keep using subpath entry points:
|
|
217
290
|
|
|
218
|
-
- `electron-effect-rpc/contract`
|
|
219
|
-
- `electron-effect-rpc/main`
|
|
220
|
-
- `electron-effect-rpc/renderer`
|
|
221
|
-
- `electron-effect-rpc/preload`
|
|
222
|
-
- `electron-effect-rpc/types`
|
|
223
|
-
- `electron-effect-rpc/testing`
|
|
291
|
+
- `electron-effect-rpc/contract` — `rpc`, `event`, `streamRpc`, `defineContract`
|
|
292
|
+
- `electron-effect-rpc/main` — `createRpcEndpoint`, `createEventPublisher`
|
|
293
|
+
- `electron-effect-rpc/renderer` — `createRpcClient`, `createEventSubscriber`, `createStreamRpcClient`
|
|
294
|
+
- `electron-effect-rpc/preload` — `exposeIpcBridge`, `createBridgeAdapters`
|
|
295
|
+
- `electron-effect-rpc/types` — shared types, `RpcDefectError`, `assertValidChannelPrefix`
|
|
296
|
+
- `electron-effect-rpc/testing` — `createInvokeStub`, `createDeferred` for unit-testing clients without Electron
|
|
224
297
|
|
|
225
298
|
## Root API Surface
|
|
226
299
|
|
|
@@ -232,7 +305,7 @@ The root entry point exports:
|
|
|
232
305
|
|
|
233
306
|
Low-level factories like `createRpcClient` remain subpath-only by design.
|
|
234
307
|
|
|
235
|
-
##
|
|
308
|
+
## Documentation
|
|
236
309
|
|
|
237
310
|
For deeper walkthroughs and production guidance:
|
|
238
311
|
|
|
@@ -241,8 +314,9 @@ For deeper walkthroughs and production guidance:
|
|
|
241
314
|
- [Typed Errors, Defects, and Diagnostics](./docs/tutorials/02-typed-errors-defects-diagnostics.md)
|
|
242
315
|
- [Events, Lifecycle, and Backpressure](./docs/tutorials/03-events-lifecycle-backpressure.md)
|
|
243
316
|
- [Streaming RPC](./docs/tutorials/04-streaming-rpc.md)
|
|
317
|
+
- [Architecture overview](./docs/architecture.md) and [ADRs](./docs/adr/) for design rationale.
|
|
244
318
|
|
|
245
|
-
## Conventions
|
|
319
|
+
## Repository Conventions
|
|
246
320
|
|
|
247
321
|
- Relative imports use `.ts` extensions.
|
|
248
322
|
- Package imports are extensionless.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Values supported by Electron's structured-clone IPC transport. */
|
|
2
|
+
export type IpcEncodedValue = string | number | bigint | boolean | null | undefined | Date | RegExp | Error | ArrayBuffer | SharedArrayBuffer | ArrayBufferView | ReadonlyArray<IpcEncodedValue> | ReadonlyMap<IpcEncodedValue, IpcEncodedValue> | ReadonlySet<IpcEncodedValue> | IpcEncodedRecord;
|
|
3
|
+
export type IpcEncodedRecord = {
|
|
4
|
+
readonly [key: string]: IpcEncodedValue;
|
|
5
|
+
};
|
|
6
|
+
/** True for non-null, non-array objects. Arrays are not IPC records. */
|
|
7
|
+
export declare function isRecord<T>(value: T): value is T & IpcEncodedRecord;
|
|
8
|
+
export declare function isIpcEncodedValue<T>(value: T): value is T & IpcEncodedValue;
|
|
9
|
+
export declare function isStringValue<T>(value: T): value is T & string;
|
|
10
|
+
export declare function isNumberValue<T>(value: T): value is T & number;
|
|
11
|
+
export declare function isFunctionValue<T>(value: T): boolean;
|
|
12
|
+
export declare function formatUnknown<T>(value: T): string;
|
|
13
|
+
export declare function parseIpcEncodedValue<T>(value: T): IpcEncodedValue | null;
|
|
14
|
+
/** Values reported through diagnostics after transport-safe normalization. */
|
|
15
|
+
export type DiagnosticCause = IpcEncodedValue;
|
|
16
|
+
export declare function toDiagnosticCause<T>(value: T): DiagnosticCause;
|
|
17
|
+
//# sourceMappingURL=boundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../src/boundary.ts"],"names":[],"mappings":"AAEA,qEAAqE;AACrE,MAAM,MAAM,eAAe,GACvB,MAAM,GACN,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,GACT,IAAI,GACJ,MAAM,GACN,KAAK,GACL,WAAW,GACX,iBAAiB,GACjB,eAAe,GACf,aAAa,CAAC,eAAe,CAAC,GAC9B,WAAW,CAAC,eAAe,EAAE,eAAe,CAAC,GAC7C,WAAW,CAAC,eAAe,CAAC,GAC5B,gBAAgB,CAAC;AAErB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;CACzC,CAAC;AAEF,wEAAwE;AACxE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,gBAAgB,CAEnE;AA4ED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,eAAe,CAE3E;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,MAAM,CAE9D;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,MAAM,CAE9D;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAEpD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAEjD;AAED,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,eAAe,GAAG,IAAI,CAExE;AAED,8EAA8E;AAC9E,MAAM,MAAM,eAAe,GAAG,eAAe,CAAC;AAE9C,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,eAAe,CAM9D"}
|
package/dist/boundary.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as Predicate from "effect/Predicate";
|
|
2
|
+
/** True for non-null, non-array objects. Arrays are not IPC records. */
|
|
3
|
+
export function isRecord(value) {
|
|
4
|
+
return Predicate.isObject(value);
|
|
5
|
+
}
|
|
6
|
+
function isPrimitiveIpcValue(value) {
|
|
7
|
+
return (Predicate.isString(value) ||
|
|
8
|
+
Predicate.isNumber(value) ||
|
|
9
|
+
Predicate.isBigInt(value) ||
|
|
10
|
+
Predicate.isBoolean(value) ||
|
|
11
|
+
value === null ||
|
|
12
|
+
value === undefined);
|
|
13
|
+
}
|
|
14
|
+
function isIpcEncodedValueInternal(value, visited) {
|
|
15
|
+
if (isPrimitiveIpcValue(value)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
if (!Predicate.isObjectOrArray(value)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (value instanceof Date ||
|
|
22
|
+
value instanceof RegExp ||
|
|
23
|
+
value instanceof Error ||
|
|
24
|
+
value instanceof ArrayBuffer ||
|
|
25
|
+
value instanceof SharedArrayBuffer ||
|
|
26
|
+
ArrayBuffer.isView(value)) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
if (value instanceof Promise || value instanceof WeakMap || value instanceof WeakSet) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (visited.has(value)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
visited.add(value);
|
|
36
|
+
if (Array.isArray(value)) {
|
|
37
|
+
return value.every((item) => isIpcEncodedValueInternal(item, visited));
|
|
38
|
+
}
|
|
39
|
+
if (value instanceof Map) {
|
|
40
|
+
for (const [key, item] of value) {
|
|
41
|
+
if (!isIpcEncodedValueInternal(key, visited) || !isIpcEncodedValueInternal(item, visited)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (value instanceof Set) {
|
|
48
|
+
for (const item of value) {
|
|
49
|
+
if (!isIpcEncodedValueInternal(item, visited)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
if (!isRecord(value)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
for (const key of Object.keys(value)) {
|
|
59
|
+
if (!isIpcEncodedValueInternal(value[key], visited)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
export function isIpcEncodedValue(value) {
|
|
66
|
+
return isIpcEncodedValueInternal(value, new WeakSet());
|
|
67
|
+
}
|
|
68
|
+
export function isStringValue(value) {
|
|
69
|
+
return Predicate.isString(value);
|
|
70
|
+
}
|
|
71
|
+
export function isNumberValue(value) {
|
|
72
|
+
return Predicate.isNumber(value);
|
|
73
|
+
}
|
|
74
|
+
export function isFunctionValue(value) {
|
|
75
|
+
return Predicate.isFunction(value);
|
|
76
|
+
}
|
|
77
|
+
export function formatUnknown(value) {
|
|
78
|
+
return value instanceof Error ? value.message : String(value);
|
|
79
|
+
}
|
|
80
|
+
export function parseIpcEncodedValue(value) {
|
|
81
|
+
return isIpcEncodedValue(value) ? value : null;
|
|
82
|
+
}
|
|
83
|
+
export function toDiagnosticCause(value) {
|
|
84
|
+
if (value instanceof Error) {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
const parsed = parseIpcEncodedValue(value);
|
|
88
|
+
return parsed !== null ? parsed : formatUnknown(value);
|
|
89
|
+
}
|
package/dist/contract.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as S from "
|
|
2
|
-
export type SchemaNoContext = S.
|
|
3
|
-
export declare const NoError:
|
|
1
|
+
import * as S from "effect/Schema";
|
|
2
|
+
export type SchemaNoContext = S.Codec<any, any, never, never>;
|
|
3
|
+
export declare const NoError: S.Never;
|
|
4
4
|
export type NoError = typeof NoError;
|
|
5
5
|
export type ErrorSchema = SchemaNoContext | NoError;
|
|
6
6
|
export declare function isNoErrorSchema(schema: ErrorSchema): schema is 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.toCodecJson<S.Exit<Res, Err, 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,19 +1,18 @@
|
|
|
1
|
-
import * as S from "
|
|
1
|
+
import * as S from "effect/Schema";
|
|
2
|
+
import * as SchemaAST from "effect/SchemaAST";
|
|
2
3
|
export const NoError = S.Never;
|
|
3
4
|
export function isNoErrorSchema(schema) {
|
|
4
|
-
|
|
5
|
+
// Also match by AST tag so a Never schema from a second copy of the
|
|
6
|
+
// schema package (npm dedup failure) is still treated as NoError.
|
|
7
|
+
return schema === NoError || SchemaAST.isNever(schema.ast);
|
|
5
8
|
}
|
|
6
9
|
export function rpc(name, req, res, err = NoError) {
|
|
7
10
|
return { name, req, res, err };
|
|
8
11
|
}
|
|
9
|
-
export function event(name, payload
|
|
10
|
-
return { name, payload
|
|
12
|
+
export function event(name, payload) {
|
|
13
|
+
return { name, payload };
|
|
11
14
|
}
|
|
12
|
-
export const exitSchemaFor = (method) => S.Exit(
|
|
13
|
-
success: method.res,
|
|
14
|
-
failure: method.err,
|
|
15
|
-
defect: S.Defect,
|
|
16
|
-
});
|
|
15
|
+
export const exitSchemaFor = (method) => S.toCodecJson(S.Exit(method.res, method.err, S.Defect()));
|
|
17
16
|
export function streamRpc(name, req, chunk, err = NoError) {
|
|
18
17
|
return { _tag: "StreamRpcMethod", name, req, chunk, err };
|
|
19
18
|
}
|
|
@@ -61,7 +60,7 @@ export function defineContract(input) {
|
|
|
61
60
|
if (duplicateMethods.length > 0) {
|
|
62
61
|
throw new Error(`Duplicate RPC method name(s): ${duplicateMethods.join(", ")}`);
|
|
63
62
|
}
|
|
64
|
-
const duplicateEvents = collectDuplicates(events.map((
|
|
63
|
+
const duplicateEvents = collectDuplicates(events.map((rpcEvent) => rpcEvent.name));
|
|
65
64
|
if (duplicateEvents.length > 0) {
|
|
66
65
|
throw new Error(`Duplicate RPC event name(s): ${duplicateEvents.join(", ")}`);
|
|
67
66
|
}
|