electron-effect-rpc 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -19
- package/dist/contract.d.ts +41 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +46 -0
- package/dist/main.d.ts +5 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +296 -0
- package/dist/preload.d.ts +15 -0
- package/dist/preload.d.ts.map +1 -0
- package/dist/preload.js +31 -0
- package/dist/protocol.d.ts +23 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +77 -0
- package/dist/renderer.d.ts +6 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +201 -0
- package/dist/testing.d.ts +15 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +17 -0
- package/dist/types.d.ts +124 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/package.json +28 -8
- package/src/contract.ts +0 -165
- package/src/main.ts +0 -169
- package/src/preload.ts +0 -33
- package/src/renderer.ts +0 -167
- package/src/testing.ts +0 -33
- package/src/types.ts +0 -134
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
function hasOwn(record, key) {
|
|
2
|
+
return Object.prototype.hasOwnProperty.call(record, key);
|
|
3
|
+
}
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === "object" && value !== null;
|
|
6
|
+
}
|
|
7
|
+
export function formatUnknown(value) {
|
|
8
|
+
return value instanceof Error ? value.message : String(value);
|
|
9
|
+
}
|
|
10
|
+
export function extractErrorTag(error) {
|
|
11
|
+
if (isRecord(error) && typeof error._tag === "string") {
|
|
12
|
+
return error._tag;
|
|
13
|
+
}
|
|
14
|
+
if (error instanceof Error && error.name.length > 0) {
|
|
15
|
+
return error.name;
|
|
16
|
+
}
|
|
17
|
+
return "RpcError";
|
|
18
|
+
}
|
|
19
|
+
export function toDefectEnvelope(cause, prefix) {
|
|
20
|
+
const causeText = formatUnknown(cause);
|
|
21
|
+
return {
|
|
22
|
+
type: "defect",
|
|
23
|
+
message: prefix ? `${prefix}: ${causeText}` : causeText,
|
|
24
|
+
cause: causeText,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function safelyCall(callback, context) {
|
|
28
|
+
if (!callback) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
callback(context);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Diagnostics hooks must never crash transport internals.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function parseRpcResponseEnvelope(value) {
|
|
39
|
+
if (!isRecord(value) || typeof value.type !== "string") {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
switch (value.type) {
|
|
43
|
+
case "success":
|
|
44
|
+
if (!hasOwn(value, "data")) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
type: "success",
|
|
49
|
+
data: value.data,
|
|
50
|
+
};
|
|
51
|
+
case "failure":
|
|
52
|
+
if (!isRecord(value.error) || typeof value.error.tag !== "string") {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (!hasOwn(value.error, "data")) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
type: "failure",
|
|
60
|
+
error: {
|
|
61
|
+
tag: value.error.tag,
|
|
62
|
+
data: value.error.data,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
case "defect":
|
|
66
|
+
if (typeof value.message !== "string") {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
type: "defect",
|
|
71
|
+
message: value.message,
|
|
72
|
+
cause: value.cause,
|
|
73
|
+
};
|
|
74
|
+
default:
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type RpcContract } from "./contract.ts";
|
|
2
|
+
import { type AnyEvent, type AnyMethod, type EventSubscriber, type EventSubscriberOptions, type RpcClient, type RpcClientOptions } from "./types.ts";
|
|
3
|
+
export declare function createRpcClient<const Methods extends ReadonlyArray<AnyMethod>, const Events extends ReadonlyArray<AnyEvent>>(contract: RpcContract<Methods, Events>, options?: RpcClientOptions): RpcClient<RpcContract<Methods, Events>>;
|
|
4
|
+
export declare function createEventSubscriber<const Methods extends ReadonlyArray<AnyMethod>, const Events extends ReadonlyArray<AnyEvent>>(contract: RpcContract<Methods, Events>, options?: EventSubscriberOptions): EventSubscriber<RpcContract<Methods, Events>>;
|
|
5
|
+
export { RpcDefectError } from "./types.ts";
|
|
6
|
+
//# sourceMappingURL=renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,WAAW,EAIjB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,SAAS,EAGd,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAE3B,KAAK,SAAS,EACd,KAAK,gBAAgB,EAEtB,MAAM,YAAY,CAAC;AAuDpB,wBAAgB,eAAe,CAC7B,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAE5C,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,EACtC,OAAO,CAAC,EAAE,gBAAgB,GACzB,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAqHzC;AAqBD,wBAAgB,qBAAqB,CACnC,KAAK,CAAC,OAAO,SAAS,aAAa,CAAC,SAAS,CAAC,EAC9C,KAAK,CAAC,MAAM,SAAS,aAAa,CAAC,QAAQ,CAAC,EAE5C,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,EACtC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,eAAe,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CA+E/C;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import * as S from "@effect/schema/Schema";
|
|
2
|
+
import { Cause, Exit } from "effect";
|
|
3
|
+
import { exitSchemaFor, isNoErrorSchema, } from "./contract.js";
|
|
4
|
+
import { formatUnknown, parseRpcResponseEnvelope, safelyCall } from "./protocol.js";
|
|
5
|
+
import { RpcDefectError, } from "./types.js";
|
|
6
|
+
function requireInvoke(options) {
|
|
7
|
+
if (!options?.invoke) {
|
|
8
|
+
throw new Error("RpcClientOptions.invoke is required.");
|
|
9
|
+
}
|
|
10
|
+
return options.invoke;
|
|
11
|
+
}
|
|
12
|
+
function requireSubscribe(options) {
|
|
13
|
+
if (!options?.subscribe) {
|
|
14
|
+
throw new Error("EventSubscriberOptions.subscribe is required.");
|
|
15
|
+
}
|
|
16
|
+
return options.subscribe;
|
|
17
|
+
}
|
|
18
|
+
function decodeLegacyExit(method, raw) {
|
|
19
|
+
const exitSchema = exitSchemaFor(method);
|
|
20
|
+
const decodeExit = S.decodeUnknownSync(exitSchema);
|
|
21
|
+
const exit = decodeExit(raw);
|
|
22
|
+
if (Exit.isSuccess(exit)) {
|
|
23
|
+
return exit.value;
|
|
24
|
+
}
|
|
25
|
+
const failureOption = Cause.failureOption(exit.cause);
|
|
26
|
+
if (failureOption._tag === "Some") {
|
|
27
|
+
throw failureOption.value;
|
|
28
|
+
}
|
|
29
|
+
const defectOption = Cause.dieOption(exit.cause);
|
|
30
|
+
if (defectOption._tag === "Some") {
|
|
31
|
+
const defect = defectOption.value;
|
|
32
|
+
if (defect instanceof Error) {
|
|
33
|
+
throw new RpcDefectError(defect.message, defect);
|
|
34
|
+
}
|
|
35
|
+
throw new RpcDefectError(String(defect), defect);
|
|
36
|
+
}
|
|
37
|
+
throw new RpcDefectError("RPC call was interrupted or failed unexpectedly", exit.cause);
|
|
38
|
+
}
|
|
39
|
+
export function createRpcClient(contract, options) {
|
|
40
|
+
const invoke = requireInvoke(options);
|
|
41
|
+
const diagnostics = options?.diagnostics;
|
|
42
|
+
const decodeMode = options?.rpcDecodeMode ?? "envelope";
|
|
43
|
+
const call = async (method, input) => {
|
|
44
|
+
let encoded;
|
|
45
|
+
try {
|
|
46
|
+
encoded = S.encodeSync(method.req)(input);
|
|
47
|
+
}
|
|
48
|
+
catch (cause) {
|
|
49
|
+
safelyCall(diagnostics?.onDecodeFailure, {
|
|
50
|
+
scope: "rpc-request",
|
|
51
|
+
name: method.name,
|
|
52
|
+
payload: input,
|
|
53
|
+
cause,
|
|
54
|
+
});
|
|
55
|
+
throw new Error(`RPC ${method.name} request encoding failed: ${formatUnknown(cause)}`);
|
|
56
|
+
}
|
|
57
|
+
const raw = await invoke(method.name, encoded);
|
|
58
|
+
const envelope = parseRpcResponseEnvelope(raw);
|
|
59
|
+
if (envelope) {
|
|
60
|
+
switch (envelope.type) {
|
|
61
|
+
case "success":
|
|
62
|
+
try {
|
|
63
|
+
return S.decodeUnknownSync(method.res)(envelope.data);
|
|
64
|
+
}
|
|
65
|
+
catch (cause) {
|
|
66
|
+
safelyCall(diagnostics?.onDecodeFailure, {
|
|
67
|
+
scope: "rpc-response",
|
|
68
|
+
name: method.name,
|
|
69
|
+
payload: envelope.data,
|
|
70
|
+
cause,
|
|
71
|
+
});
|
|
72
|
+
throw new Error(`RPC ${method.name} success payload decoding failed: ${formatUnknown(cause)}`);
|
|
73
|
+
}
|
|
74
|
+
case "failure":
|
|
75
|
+
if (isNoErrorSchema(method.err)) {
|
|
76
|
+
throw new RpcDefectError(`RPC ${method.name} received a failure for a method that declares NoError`, envelope.error);
|
|
77
|
+
}
|
|
78
|
+
let decodedError;
|
|
79
|
+
try {
|
|
80
|
+
decodedError = S.decodeUnknownSync(method.err)(envelope.error.data);
|
|
81
|
+
}
|
|
82
|
+
catch (cause) {
|
|
83
|
+
safelyCall(diagnostics?.onDecodeFailure, {
|
|
84
|
+
scope: "rpc-response",
|
|
85
|
+
name: method.name,
|
|
86
|
+
payload: envelope.error,
|
|
87
|
+
cause,
|
|
88
|
+
});
|
|
89
|
+
throw new Error(`RPC ${method.name} failure payload decoding failed: ${formatUnknown(cause)}`);
|
|
90
|
+
}
|
|
91
|
+
throw decodedError;
|
|
92
|
+
case "defect":
|
|
93
|
+
throw new RpcDefectError(envelope.message, envelope.cause);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (decodeMode === "dual") {
|
|
97
|
+
try {
|
|
98
|
+
return decodeLegacyExit(method, raw);
|
|
99
|
+
}
|
|
100
|
+
catch (cause) {
|
|
101
|
+
safelyCall(diagnostics?.onProtocolError, {
|
|
102
|
+
method: method.name,
|
|
103
|
+
response: raw,
|
|
104
|
+
cause,
|
|
105
|
+
});
|
|
106
|
+
throw cause;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const cause = new Error(`RPC ${method.name} response was not a valid envelope.`);
|
|
110
|
+
safelyCall(diagnostics?.onProtocolError, {
|
|
111
|
+
method: method.name,
|
|
112
|
+
response: raw,
|
|
113
|
+
cause,
|
|
114
|
+
});
|
|
115
|
+
throw cause;
|
|
116
|
+
};
|
|
117
|
+
const client = Object.create(null);
|
|
118
|
+
const clientRecord = client;
|
|
119
|
+
for (const method of contract.methods) {
|
|
120
|
+
const caller = (input) => {
|
|
121
|
+
const payload = input ?? S.decodeUnknownSync(method.req)({});
|
|
122
|
+
return call(method, payload);
|
|
123
|
+
};
|
|
124
|
+
clientRecord[method.name] = caller;
|
|
125
|
+
}
|
|
126
|
+
return client;
|
|
127
|
+
}
|
|
128
|
+
function reportDecodeFailure(mode, eventName, payload, cause, options) {
|
|
129
|
+
safelyCall(options?.diagnostics?.onDecodeFailure, {
|
|
130
|
+
scope: "event-payload",
|
|
131
|
+
name: eventName,
|
|
132
|
+
payload,
|
|
133
|
+
cause,
|
|
134
|
+
});
|
|
135
|
+
if (mode === "strict") {
|
|
136
|
+
throw cause;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export function createEventSubscriber(contract, options) {
|
|
140
|
+
const subscribe = requireSubscribe(options);
|
|
141
|
+
const mode = options?.decodeMode ?? "safe";
|
|
142
|
+
const eventMap = new Map();
|
|
143
|
+
const subscriptions = new Set();
|
|
144
|
+
for (const event of contract.events) {
|
|
145
|
+
eventMap.set(event.name, event);
|
|
146
|
+
}
|
|
147
|
+
function registerUnsubscribe(unsubscribe) {
|
|
148
|
+
subscriptions.add(unsubscribe);
|
|
149
|
+
return () => {
|
|
150
|
+
if (subscriptions.delete(unsubscribe)) {
|
|
151
|
+
unsubscribe();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const subscribeEvent = (event, handler) => {
|
|
156
|
+
const decoder = S.decodeUnknownSync(event.payload);
|
|
157
|
+
const unsubscribe = subscribe(event.name, (payload) => {
|
|
158
|
+
let decoded;
|
|
159
|
+
try {
|
|
160
|
+
decoded = decoder(payload);
|
|
161
|
+
}
|
|
162
|
+
catch (cause) {
|
|
163
|
+
reportDecodeFailure(mode, event.name, payload, cause, options);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
handler(decoded);
|
|
167
|
+
});
|
|
168
|
+
return registerUnsubscribe(unsubscribe);
|
|
169
|
+
};
|
|
170
|
+
const subscribeByName = (name, handler) => {
|
|
171
|
+
const event = eventMap.get(name);
|
|
172
|
+
if (!event) {
|
|
173
|
+
throw new Error(`Unknown event: ${name}`);
|
|
174
|
+
}
|
|
175
|
+
const decoder = S.decodeUnknownSync(event.payload);
|
|
176
|
+
const unsubscribe = subscribe(name, (payload) => {
|
|
177
|
+
let decoded;
|
|
178
|
+
try {
|
|
179
|
+
decoded = decoder(payload);
|
|
180
|
+
}
|
|
181
|
+
catch (cause) {
|
|
182
|
+
reportDecodeFailure(mode, name, payload, cause, options);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
handler(decoded);
|
|
186
|
+
});
|
|
187
|
+
return registerUnsubscribe(unsubscribe);
|
|
188
|
+
};
|
|
189
|
+
function dispose() {
|
|
190
|
+
for (const unsubscribe of subscriptions) {
|
|
191
|
+
unsubscribe();
|
|
192
|
+
}
|
|
193
|
+
subscriptions.clear();
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
subscribe: subscribeEvent,
|
|
197
|
+
subscribeByName,
|
|
198
|
+
dispose,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
export { RpcDefectError } from "./types.js";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RpcInvoke } from "./types.ts";
|
|
2
|
+
export type Invocation = {
|
|
3
|
+
readonly method: string;
|
|
4
|
+
readonly payload: unknown;
|
|
5
|
+
};
|
|
6
|
+
export type InvokeStub = RpcInvoke & {
|
|
7
|
+
readonly invocations: Invocation[];
|
|
8
|
+
};
|
|
9
|
+
export declare const createInvokeStub: (impl: RpcInvoke) => InvokeStub;
|
|
10
|
+
export declare const createDeferred: <T>() => {
|
|
11
|
+
promise: Promise<T>;
|
|
12
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
13
|
+
reject: (reason?: unknown) => void;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC;AAE5E,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS,KAAG,UAYlD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,CAAC;;qBACT,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI;sBAC1B,OAAO,KAAK,IAAI;CAOvC,CAAC"}
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const createInvokeStub = (impl) => {
|
|
2
|
+
const invocations = [];
|
|
3
|
+
const wrapped = Object.assign(async (method, payload) => {
|
|
4
|
+
invocations.push({ method, payload });
|
|
5
|
+
return impl(method, payload);
|
|
6
|
+
}, { invocations });
|
|
7
|
+
return wrapped;
|
|
8
|
+
};
|
|
9
|
+
export const createDeferred = () => {
|
|
10
|
+
let resolve = () => { };
|
|
11
|
+
let reject = () => { };
|
|
12
|
+
const promise = new Promise((res, rej) => {
|
|
13
|
+
resolve = res;
|
|
14
|
+
reject = rej;
|
|
15
|
+
});
|
|
16
|
+
return { promise, resolve, reject };
|
|
17
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type * as Effect from "effect/Effect";
|
|
2
|
+
import type * as Runtime from "effect/Runtime";
|
|
3
|
+
import type { AnyEvent, AnyMethod, ExtractMethod, RpcContract, RpcError, RpcEventPayload, RpcInput, RpcOutput } from "./contract.ts";
|
|
4
|
+
export type { AnyEvent, AnyMethod, ErrorSchema, ExtractMethod, RpcContract, RpcError, RpcEvent, RpcEventPayload, RpcInput, RpcMethod, RpcOutput, SchemaNoContext, } from "./contract.ts";
|
|
5
|
+
export type Implementations<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[]>, R = never> = {
|
|
6
|
+
readonly [Name in C["methods"][number]["name"]]: (input: RpcInput<ExtractMethod<C["methods"], Name>>) => Effect.Effect<RpcOutput<ExtractMethod<C["methods"], Name>>, RpcError<ExtractMethod<C["methods"], Name>>, R>;
|
|
7
|
+
};
|
|
8
|
+
type IsEmptyObject<T> = keyof T extends never ? true : false;
|
|
9
|
+
export type RpcCaller<M extends AnyMethod> = IsEmptyObject<RpcInput<M>> extends true ? () => Promise<RpcOutput<M>> : (input: RpcInput<M>) => Promise<RpcOutput<M>>;
|
|
10
|
+
export type RpcClient<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[]>> = {
|
|
11
|
+
readonly [Name in C["methods"][number]["name"]]: RpcCaller<ExtractMethod<C["methods"], Name>>;
|
|
12
|
+
};
|
|
13
|
+
export declare class RpcDefectError extends Error {
|
|
14
|
+
readonly cause: unknown;
|
|
15
|
+
readonly _tag = "RpcDefectError";
|
|
16
|
+
constructor(message: string, cause: unknown);
|
|
17
|
+
}
|
|
18
|
+
export type ChannelPrefix = {
|
|
19
|
+
readonly rpc: string;
|
|
20
|
+
readonly event: string;
|
|
21
|
+
};
|
|
22
|
+
export declare const defaultChannelPrefix: ChannelPrefix;
|
|
23
|
+
export type DecodeFailureScope = "rpc-request" | "rpc-response" | "event-payload";
|
|
24
|
+
export type DecodeFailureContext = {
|
|
25
|
+
readonly scope: DecodeFailureScope;
|
|
26
|
+
readonly name: string;
|
|
27
|
+
readonly payload: unknown;
|
|
28
|
+
readonly cause: unknown;
|
|
29
|
+
};
|
|
30
|
+
export type ProtocolErrorContext = {
|
|
31
|
+
readonly method: string;
|
|
32
|
+
readonly response: unknown;
|
|
33
|
+
readonly cause: unknown;
|
|
34
|
+
};
|
|
35
|
+
export type DispatchFailureContext = {
|
|
36
|
+
readonly event: string;
|
|
37
|
+
readonly payload: unknown;
|
|
38
|
+
readonly cause: unknown;
|
|
39
|
+
};
|
|
40
|
+
export type DroppedEventReason = "queue_full" | "encoding_failed";
|
|
41
|
+
export type DroppedEventContext = {
|
|
42
|
+
readonly event: string;
|
|
43
|
+
readonly payload: unknown;
|
|
44
|
+
readonly reason: DroppedEventReason;
|
|
45
|
+
readonly queued: number;
|
|
46
|
+
readonly dropped: number;
|
|
47
|
+
};
|
|
48
|
+
export type RpcInvoke = (method: string, payload: unknown) => Promise<unknown>;
|
|
49
|
+
export type RpcResponseDecodeMode = "envelope" | "dual";
|
|
50
|
+
export type RpcClientDiagnostics = {
|
|
51
|
+
readonly onDecodeFailure?: (context: DecodeFailureContext) => void;
|
|
52
|
+
readonly onProtocolError?: (context: ProtocolErrorContext) => void;
|
|
53
|
+
};
|
|
54
|
+
export type RpcClientOptions = {
|
|
55
|
+
readonly invoke?: RpcInvoke;
|
|
56
|
+
readonly diagnostics?: RpcClientDiagnostics;
|
|
57
|
+
readonly rpcDecodeMode?: RpcResponseDecodeMode;
|
|
58
|
+
};
|
|
59
|
+
export type RpcEndpointDiagnostics = {
|
|
60
|
+
readonly onDecodeFailure?: (context: DecodeFailureContext) => void;
|
|
61
|
+
readonly onProtocolError?: (context: ProtocolErrorContext) => void;
|
|
62
|
+
};
|
|
63
|
+
export type IpcMainLike = {
|
|
64
|
+
readonly handle: (channel: string, listener: (event: unknown, payload: unknown) => unknown) => unknown;
|
|
65
|
+
readonly removeHandler: (channel: string) => unknown;
|
|
66
|
+
};
|
|
67
|
+
export interface RpcEndpoint {
|
|
68
|
+
readonly start: () => void;
|
|
69
|
+
readonly stop: () => void;
|
|
70
|
+
readonly dispose: () => void;
|
|
71
|
+
readonly isRunning: () => boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Runtime used to execute handler effects.
|
|
75
|
+
*/
|
|
76
|
+
export type RpcEndpointOptions<R = never> = {
|
|
77
|
+
readonly channelPrefix?: ChannelPrefix;
|
|
78
|
+
readonly runtime: Runtime.Runtime<R>;
|
|
79
|
+
readonly diagnostics?: RpcEndpointDiagnostics;
|
|
80
|
+
};
|
|
81
|
+
export type EventPublisherDiagnostics = {
|
|
82
|
+
readonly onDecodeFailure?: (context: DecodeFailureContext) => void;
|
|
83
|
+
readonly onDispatchFailure?: (context: DispatchFailureContext) => void;
|
|
84
|
+
readonly onDroppedEvent?: (context: DroppedEventContext) => void;
|
|
85
|
+
};
|
|
86
|
+
export type RendererWindowLike = {
|
|
87
|
+
readonly isDestroyed: () => boolean;
|
|
88
|
+
readonly webContents: {
|
|
89
|
+
readonly send: (channel: string, payload: unknown) => void;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
export type EventPublisherOptions = {
|
|
93
|
+
readonly channelPrefix?: ChannelPrefix;
|
|
94
|
+
readonly getWindow: () => RendererWindowLike | null;
|
|
95
|
+
readonly maxQueueSize?: number;
|
|
96
|
+
readonly diagnostics?: EventPublisherDiagnostics;
|
|
97
|
+
};
|
|
98
|
+
export interface RpcEventPublisher<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[]>> {
|
|
99
|
+
readonly publish: <E extends C["events"][number]>(event: E, payload: RpcEventPayload<E>) => Effect.Effect<void, never>;
|
|
100
|
+
readonly start: () => void;
|
|
101
|
+
readonly stop: () => void;
|
|
102
|
+
readonly dispose: () => void;
|
|
103
|
+
readonly isRunning: () => boolean;
|
|
104
|
+
readonly stats: () => {
|
|
105
|
+
readonly queued: number;
|
|
106
|
+
readonly dropped: number;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export type EventDecodeMode = "safe" | "strict";
|
|
110
|
+
export type EventSubscribe = (name: string, handler: (payload: unknown) => void) => () => void;
|
|
111
|
+
export type EventSubscriberDiagnostics = {
|
|
112
|
+
readonly onDecodeFailure?: (context: DecodeFailureContext) => void;
|
|
113
|
+
};
|
|
114
|
+
export type EventSubscriberOptions = {
|
|
115
|
+
readonly subscribe?: EventSubscribe;
|
|
116
|
+
readonly decodeMode?: EventDecodeMode;
|
|
117
|
+
readonly diagnostics?: EventSubscriberDiagnostics;
|
|
118
|
+
};
|
|
119
|
+
export interface EventSubscriber<C extends RpcContract<readonly AnyMethod[], readonly AnyEvent[]>> {
|
|
120
|
+
readonly subscribe: <E extends C["events"][number]>(event: E, handler: (payload: RpcEventPayload<E>) => void) => () => void;
|
|
121
|
+
readonly subscribeByName: (name: string, handler: (payload: unknown) => void) => () => void;
|
|
122
|
+
readonly dispose: () => void;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class RpcDefectError extends Error {
|
|
2
|
+
constructor(message, cause) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.cause = cause;
|
|
5
|
+
this._tag = "RpcDefectError";
|
|
6
|
+
this.name = "RpcDefectError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export const defaultChannelPrefix = {
|
|
10
|
+
rpc: "rpc/",
|
|
11
|
+
event: "event/",
|
|
12
|
+
};
|
package/package.json
CHANGED
|
@@ -1,20 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electron-effect-rpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Typed IPC RPC for Electron, built on Effect and @effect/schema",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
|
-
"./main":
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"./
|
|
12
|
-
|
|
7
|
+
"./main": {
|
|
8
|
+
"types": "./dist/main.d.ts",
|
|
9
|
+
"default": "./dist/main.js"
|
|
10
|
+
},
|
|
11
|
+
"./renderer": {
|
|
12
|
+
"types": "./dist/renderer.d.ts",
|
|
13
|
+
"default": "./dist/renderer.js"
|
|
14
|
+
},
|
|
15
|
+
"./preload": {
|
|
16
|
+
"types": "./dist/preload.d.ts",
|
|
17
|
+
"default": "./dist/preload.js"
|
|
18
|
+
},
|
|
19
|
+
"./contract": {
|
|
20
|
+
"types": "./dist/contract.d.ts",
|
|
21
|
+
"default": "./dist/contract.js"
|
|
22
|
+
},
|
|
23
|
+
"./types": {
|
|
24
|
+
"types": "./dist/types.d.ts",
|
|
25
|
+
"default": "./dist/types.js"
|
|
26
|
+
},
|
|
27
|
+
"./testing": {
|
|
28
|
+
"types": "./dist/testing.d.ts",
|
|
29
|
+
"default": "./dist/testing.js"
|
|
30
|
+
}
|
|
13
31
|
},
|
|
14
32
|
"files": [
|
|
15
|
-
"
|
|
33
|
+
"dist"
|
|
16
34
|
],
|
|
17
35
|
"scripts": {
|
|
36
|
+
"build": "tsc",
|
|
37
|
+
"prepublishOnly": "bun run build",
|
|
18
38
|
"test": "bun test",
|
|
19
39
|
"typecheck": "tsc --noEmit"
|
|
20
40
|
},
|