@trevrpc/effect 1.5.0 → 1.6.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 CHANGED
@@ -1,9 +1,9 @@
1
1
  # @trevrpc/effect
2
2
 
3
- [Effect](https://effect.website) adapter for the TrevRPC browser client, [`@trevrpc/trevrpc-js`](https://www.npmjs.com/package/@trevrpc/trevrpc-js): it wraps a generated client so that its calls are effects and streams.
3
+ [Effect](https://effect.website) adapter for the TrevRPC browser client, [`@trevrpc/trevrpc-js`](https://www.npmjs.com/package/@trevrpc/trevrpc-js): it makes clients whose calls are effects and streams.
4
4
 
5
5
  ```sh
6
- npm install @trevrpc/effect @trevrpc/trevrpc-js effect
6
+ npm install @trevrpc/effect @trevrpc/trevrpc-js @bufbuild/protobuf effect
7
7
  ```
8
8
 
9
9
  ```ts
@@ -11,10 +11,10 @@ import { createChannel } from "@trevrpc/trevrpc-js";
11
11
  import { createEffectClient } from "@trevrpc/effect";
12
12
  import { Stream } from "effect";
13
13
 
14
- import { GreeterClient, GreeterService } from "./gen/greeter/v1/greeter.trevrpc.js";
14
+ import { Greeter } from "./gen/greeter/v1/greeter_pb.js";
15
15
 
16
16
  const channel = createChannel("https://example.com/trevrpc");
17
- const greeter = createEffectClient(GreeterService, new GreeterClient(channel));
17
+ const greeter = createEffectClient(Greeter, channel);
18
18
 
19
19
  // Effect<HelloReply, TrevRpcError>
20
20
  const reply = greeter.sayHello({ name: "Ada" });
@@ -28,7 +28,7 @@ const replies = greeter.chat(
28
28
  );
29
29
  ```
30
30
 
31
- `createEffectClient(service, client)` takes the generated descriptor and client, and returns a client with the same method names:
31
+ `createEffectClient(service, transport, options?)` takes what `createClient` of `@trevrpc/trevrpc-js` takes: a service descriptor that [protobuf-es](https://github.com/bufbuild/protobuf-es)'s `protoc-gen-es` generates, a transport such as a channel, and default call options and interceptors. It returns a client with one method per RPC, named by its `localName`:
32
32
 
33
33
  | RPC kind | Effect method |
34
34
  | ----------------------- | --------------------------------------------------------------------------------------- |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trevrpc/effect",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Effect adapter for TrevRPC browser clients",
5
5
  "homepage": "https://trev.zip/llc/TrevRPC",
6
6
  "bugs": {
@@ -39,16 +39,19 @@
39
39
  "check": "npm run format:check && npm run lint && npm run typecheck && npm test"
40
40
  },
41
41
  "devDependencies": {
42
+ "@bufbuild/protobuf": "^2.17.0",
43
+ "@bufbuild/protoc-gen-es": "^2.17.0",
42
44
  "@trevrpc/trevrpc-js": "file:..",
43
45
  "effect": "^4.1.2",
44
46
  "typescript": "^7.0.2"
45
47
  },
46
48
  "peerDependencies": {
47
- "@trevrpc/trevrpc-js": "^1.5.0",
49
+ "@bufbuild/protobuf": "^2.17.0",
50
+ "@trevrpc/trevrpc-js": "^1.6.0",
48
51
  "effect": "^4.1.2"
49
52
  },
50
53
  "engines": {
51
54
  "node": ">=24"
52
55
  },
53
- "packageManager": "npm@12.2.1"
56
+ "packageManager": "npm@12.2.2"
54
57
  }
package/src/index.d.ts CHANGED
@@ -1,56 +1,55 @@
1
- import type {
2
- BidirectionalStreamingCall,
3
- CallOptions,
4
- ClientStreamingCall,
5
- ResponseStream,
6
- RpcServiceDescriptor,
7
- TrevRpcError,
8
- } from "@trevrpc/trevrpc-js";
1
+ import type { DescMessage, DescService, MessageInitShape, MessageShape } from "@bufbuild/protobuf";
2
+ import type { CallOptions, ClientOptions, Transport, TrevRpcError } from "@trevrpc/trevrpc-js";
9
3
  import type { Effect, Stream } from "effect";
10
4
 
11
5
  /**
12
- * The Effect form of a generated client method:
6
+ * The Effect form of a method of a protobuf-es service descriptor:
13
7
  *
14
8
  * - unary: `(request, options?) => Effect<Response, TrevRpcError>`
15
9
  * - server streaming: `(request, options?) => Stream<Response, TrevRpcError>`
16
10
  * - client streaming: `(requests, options?) => Effect<Response, TrevRpcError | E, R>`
17
11
  * - bidirectional streaming: `(requests, options?) => Stream<Response, TrevRpcError | E, R>`
18
12
  *
19
- * where `requests` is a `Stream<Request, E, R>`. `never` for anything else.
13
+ * where a request is a message or the init object `create()` takes, `requests` is a
14
+ * `Stream<Request, E, R>`, and a response is a message.
20
15
  */
21
- export type EffectMethod<Method> = Method extends (
22
- options?: CallOptions,
23
- ) => Promise<BidirectionalStreamingCall<infer Request, infer Response>>
24
- ? <E = never, R = never>(
25
- requests: Stream.Stream<Request, E, R>,
26
- options?: CallOptions,
27
- ) => Stream.Stream<Response, TrevRpcError | E, R>
28
- : Method extends (
16
+ export type EffectMethod<Method> = Method extends {
17
+ methodKind: infer Kind;
18
+ input: infer I extends DescMessage;
19
+ output: infer O extends DescMessage;
20
+ }
21
+ ? Kind extends "unary"
22
+ ? (
23
+ request: MessageInitShape<I>,
29
24
  options?: CallOptions,
30
- ) => Promise<ClientStreamingCall<infer Request, infer Response>>
31
- ? <E = never, R = never>(
32
- requests: Stream.Stream<Request, E, R>,
33
- options?: CallOptions,
34
- ) => Effect.Effect<Response, TrevRpcError | E, R>
35
- : Method extends (
36
- request: infer Request,
25
+ ) => Effect.Effect<MessageShape<O>, TrevRpcError>
26
+ : Kind extends "server_streaming"
27
+ ? (
28
+ request: MessageInitShape<I>,
37
29
  options?: CallOptions,
38
- ) => Promise<ResponseStream<infer Response>>
39
- ? (request: Request, options?: CallOptions) => Stream.Stream<Response, TrevRpcError>
40
- : Method extends (request: infer Request, options?: CallOptions) => Promise<infer Response>
41
- ? (request: Request, options?: CallOptions) => Effect.Effect<Response, TrevRpcError>
42
- : never;
30
+ ) => Stream.Stream<MessageShape<O>, TrevRpcError>
31
+ : Kind extends "client_streaming"
32
+ ? <E = never, R = never>(
33
+ requests: Stream.Stream<MessageInitShape<I>, E, R>,
34
+ options?: CallOptions,
35
+ ) => Effect.Effect<MessageShape<O>, TrevRpcError | E, R>
36
+ : Kind extends "bidi_streaming"
37
+ ? <E = never, R = never>(
38
+ requests: Stream.Stream<MessageInitShape<I>, E, R>,
39
+ options?: CallOptions,
40
+ ) => Stream.Stream<MessageShape<O>, TrevRpcError | E, R>
41
+ : never
42
+ : never;
43
43
 
44
- /** A generated client with its RPC methods in Effect form; other members are left out. */
45
- export type EffectClient<Client> = {
46
- [Name in keyof Client as EffectMethod<Client[Name]> extends never ? never : Name]: EffectMethod<
47
- Client[Name]
48
- >;
44
+ /** The Effect client of a service: one method per RPC, named by its `localName`. */
45
+ export type EffectClient<S extends DescService> = {
46
+ [Name in keyof S["method"]]: EffectMethod<S["method"][Name]>;
49
47
  };
50
48
 
51
49
  /**
52
- * Wraps a generated client's methods in Effect. `service` is the client's generated descriptor,
53
- * such as `GreeterService` for a `GreeterClient`.
50
+ * Creates a client for `service`, a service descriptor that protoc-gen-es generates (such as
51
+ * `Greeter` from `greeter_pb.js`), whose calls are Effect values. `transport` and `options` are
52
+ * those of `createClient` in `@trevrpc/trevrpc-js`.
54
53
  *
55
54
  * Interrupting an effect or stream cancels its call, which the server sees as `Code.Cancelled`.
56
55
  * A `signal` in the call options cancels it too. Client-streaming and bidirectional calls pull
@@ -62,12 +61,13 @@ export type EffectClient<Client> = {
62
61
  * Calls fail with a `TrevRpcError`; anything else a call rejects with becomes one with
63
62
  * `Code.Unknown`, and with the original as `cause`.
64
63
  *
65
- * @throws TypeError if `client` lacks a method of `service`.
64
+ * @throws TrevRpcError with `Code.InvalidArgument` where `createClient` throws it.
66
65
  */
67
- export function createEffectClient<Client extends object>(
68
- service: RpcServiceDescriptor,
69
- client: Client,
70
- ): EffectClient<Client>;
66
+ export function createEffectClient<S extends DescService>(
67
+ service: S,
68
+ transport: Transport,
69
+ options?: ClientOptions,
70
+ ): EffectClient<S>;
71
71
 
72
72
  /** Returns a `TrevRpcError` as is, and wraps anything else in one with `Code.Unknown`. */
73
73
  export function toTrevRpcError(error: unknown): TrevRpcError;
package/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Code, TrevRpcError } from "@trevrpc/trevrpc-js";
1
+ import { Code, TrevRpcError, createClient } from "@trevrpc/trevrpc-js";
2
2
  import { Effect, Stream } from "effect";
3
3
 
4
4
  /** Returns a `TrevRpcError` as is, and wraps anything else in one with `Code.Unknown`. */
@@ -11,39 +11,30 @@ export function toTrevRpcError(error) {
11
11
  }
12
12
 
13
13
  /**
14
- * Wraps a generated client's methods in Effect: unary calls return an `Effect`, server-streaming
15
- * calls a `Stream`, and client-streaming and bidirectional calls take their requests as a
16
- * `Stream`. Interrupting the effect or stream cancels the call.
14
+ * Creates a client for `service`, a service descriptor that protoc-gen-es generates, whose calls
15
+ * are Effect values: unary calls return an `Effect`, server-streaming calls a `Stream`, and
16
+ * client-streaming and bidirectional calls take their requests as a `Stream`. Interrupting the
17
+ * effect or stream cancels the call. `transport` and `options` are those of `createClient`.
17
18
  */
18
- export function createEffectClient(service, client) {
19
+ export function createEffectClient(service, transport, options) {
20
+ const client = createClient(service, transport, options);
19
21
  const result = {};
20
- for (const [name, method] of Object.entries(service.methods)) {
21
- if (typeof client[name] !== "function") {
22
- throw new TypeError(`client has no ${name} method for ${service.fullName}/${method.name}`);
23
- }
24
- const call = client[name].bind(client);
25
- switch (method.kind) {
26
- case "unary":
27
- result[name] = liftUnary(call);
28
- break;
29
- case "serverStreaming":
30
- result[name] = liftServerStreaming(call);
31
- break;
32
- case "clientStreaming":
33
- result[name] = liftClientStreaming(call);
34
- break;
35
- case "bidirectionalStreaming":
36
- result[name] = liftBidirectionalStreaming(call);
37
- break;
38
- default:
39
- throw new TypeError(
40
- `unknown kind ${String(method.kind)} of ${service.fullName}/${method.name}`,
41
- );
42
- }
22
+ for (const method of service.methods) {
23
+ const call = client[method.localName];
24
+ result[method.localName] = Lifts[method.methodKind](call);
43
25
  }
44
26
  return result;
45
27
  }
46
28
 
29
+ /** How each protobuf-es method kind's calls become Effect values. */
30
+ const Lifts = Object.freeze({
31
+ __proto__: null,
32
+ unary: liftUnary,
33
+ server_streaming: liftServerStreaming,
34
+ client_streaming: liftClientStreaming,
35
+ bidi_streaming: liftBidirectionalStreaming,
36
+ });
37
+
47
38
  function liftUnary(call) {
48
39
  return (request, options) =>
49
40
  Effect.tryPromise({