@shivaedev/errors 0.6.0 → 0.7.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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0 - 2026-11-03
4
+
5
+ ### Breaking
6
+
7
+ - `rpcBoundaryHandlers(group, options, handlers)` derives each operation's rejection schema independently. Replace the former `rpcBoundary(schema, options)` second argument with `options`; every operation must declare the fallback returned by `unexpected`. Effect and Stream handlers retain their own rejection types and required services.
8
+
3
9
  ## 0.6.0 - 2026-11-03
4
10
 
5
11
  ### Added
package/README.md CHANGED
@@ -70,19 +70,23 @@ export const Handlers = Api.toLayer({
70
70
 
71
71
  `storageSave` is the application's Effect. `unexpected` constructs a member of the declared schema from the public message. The default message is `Internal server error`; `message` supplies application text, with credential-shaped strings redacted. Before masking an unexpected failure, the boundary reports its diagnostic-redacted cause through Effect's registered `ErrorReporter`s at `Error` severity. Request tracing logs that masked failure at ERROR with its redacted stack, while declared rejections stay at INFO. The original Effect cause is kept on the server rejection's non-enumerable `cause`, so a server span can trace it. Keep `cause` out of the wire schema, as the taxonomy classes do. `ServiceUnavailable` describes a declared availability failure without implying a session-provider outage.
72
72
 
73
- Apply the same boundary to an entire handler registration with `rpcBoundaryHandlers(group, boundary, handlers)`:
73
+ Apply boundary options to an entire handler registration with `rpcBoundaryHandlers(group, options, handlers)`:
74
74
 
75
75
  ```ts
76
76
  import { rpcBoundaryHandlers } from "@shivaedev/errors/rpcBoundaryHandlers.ts";
77
77
 
78
78
  export const Handlers = Api.toLayer(
79
- rpcBoundaryHandlers(Api, expose, {
80
- Save: () => storageSave,
81
- }),
79
+ rpcBoundaryHandlers(
80
+ Api,
81
+ { unexpected: (message) => new InternalServerError({ message }) },
82
+ {
83
+ Save: () => storageSave,
84
+ },
85
+ ),
82
86
  );
83
87
  ```
84
88
 
85
- The group supplies payload and result types. Each RPC must declare the boundary's rejection schema. Effect and Stream handlers share the mapping, including failures after a stream has delivered values; native `Rpc.Wrapper` options remain available. Handler service requirements pass through to the native handler Layer. Use this map inside an Effect passed to `toLayer` when handler construction needs services. Queue-backed streams and Deferred results use native handlers with an explicit boundary at their producer instead.
89
+ The group supplies payload, result and rejection types for each operation. Each handler keeps only its own declared rejections; a rejection declared by another operation is unexpected here. Every operation must declare the fallback returned by `unexpected`. Effect and Stream handlers share the mapping, including failures after a stream has delivered values; native `Rpc.Wrapper` options remain available. Handler service requirements pass through to the native handler Layer. Use this map inside an Effect passed to `toLayer` when handler construction needs services. Queue-backed streams and Deferred results use native handlers with an explicit boundary at their producer instead.
86
90
 
87
91
  ### Masking secrets in failures
88
92
 
@@ -3,7 +3,5 @@ export interface RpcBoundaryOptions<E> {
3
3
  readonly message?: string;
4
4
  readonly unexpected: (message: string) => E;
5
5
  }
6
- export declare function rpcBoundary<TSchema extends Schema.ConstraintDecoder<{
7
- readonly message: string;
8
- }>>(schema: TSchema, options: RpcBoundaryOptions<TSchema["Type"]>): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, TSchema["Type"], R>;
6
+ export declare function rpcBoundary<TSchema extends Schema.Constraint>(schema: TSchema, options: RpcBoundaryOptions<TSchema["Type"]>): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, TSchema["Type"], R>;
9
7
  //# sourceMappingURL=boundary.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../src/boundary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,MAAM,EAA4B,MAAM,EAAE,MAAM,QAAQ,CAAC;AAkClF,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,CAAC;CAC5C;AACD,wBAAgB,WAAW,CAC1B,OAAO,SAAS,MAAM,CAAC,iBAAiB,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB,CAAC,EACD,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAEtD,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAkBpF"}
1
+ {"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../src/boundary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,MAAM,EAA4B,MAAM,EAAE,MAAM,QAAQ,CAAC;AAkClF,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,CAAC;CAC5C;AACD,wBAAgB,WAAW,CAAC,OAAO,SAAS,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAEnH,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAkBpF"}
@@ -1 +1 @@
1
- {"version":3,"file":"boundary.js","sourceRoot":"","sources":["../src/boundary.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"boundary.js","sourceRoot":"","sources":["../src/boundary.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,19 @@
1
- import { Effect, Stream } from "effect";
1
+ import { Effect, Stream, type Types } from "effect";
2
2
  import { Rpc, type RpcGroup, RpcSchema } from "effect/rpc";
3
+ import { type RpcBoundaryOptions } from "#boundary.ts";
3
4
  type Handlers<R extends Rpc.Any> = {
4
5
  readonly [TRpc in R as TRpc["_tag"]]: (...args: Parameters<Rpc.ToHandlerFn<TRpc>>) => Rpc.WrapperOr<TRpc extends {
5
6
  readonly successSchema: RpcSchema.Stream<infer TSuccess, infer _E>;
6
7
  } ? Stream.Stream<TSuccess["Type"], unknown, unknown> : Effect.Effect<Rpc.Success<TRpc>, unknown, unknown>>;
7
8
  };
8
9
  type Bounded<T, E> = T extends Effect.Effect<infer A, infer _E, infer R> ? Effect.Effect<A, E, R> : T extends Stream.Stream<infer A, infer _E, infer R> ? Stream.Stream<A, E, R> : T extends Rpc.Wrapper<infer TValue> ? Rpc.Wrapper<Bounded<TValue, E>> : never;
9
- type BoundedHandlers<THandlers, E> = {
10
- readonly [TKey in keyof THandlers]: THandlers[TKey] extends (...args: infer TArgs) => infer TValue ? (...args: TArgs) => Bounded<TValue, E> : never;
10
+ type Rejection<TRpc extends Rpc.Any> = TRpc extends Rpc.AnyWithProps ? TRpc["errorSchema"]["Type"] | (TRpc["successSchema"] extends RpcSchema.Stream<infer _A, infer E> ? E["Type"] : never) : never;
11
+ type BoundedHandlers<TRpcs extends Rpc.Any, THandlers> = {
12
+ readonly [TKey in keyof THandlers]: THandlers[TKey] extends (...args: infer TArgs) => infer TValue ? (...args: TArgs) => Bounded<TValue, Rejection<Extract<TRpcs, {
13
+ readonly _tag: TKey;
14
+ }>>> : never;
11
15
  };
12
- export declare function rpcBoundaryHandlers<TRpcs extends Rpc.Any, E, THandlers extends Handlers<TRpcs>>(group: RpcGroup.RpcGroup<TRpcs>, boundary: <A, TError, R>(effect: Effect.Effect<A, TError, R>) => Effect.Effect<A, E, R>, handlers: THandlers): BoundedHandlers<THandlers, E>;
16
+ type GroupOptions<TRpcs extends Rpc.Any> = Types.UnionToIntersection<TRpcs extends unknown ? RpcBoundaryOptions<Rejection<TRpcs>> : never>;
17
+ export declare function rpcBoundaryHandlers<TRpcs extends Rpc.Any, THandlers extends Handlers<TRpcs>>(group: RpcGroup.RpcGroup<TRpcs>, options: NoInfer<GroupOptions<TRpcs>>, handlers: THandlers): BoundedHandlers<TRpcs, THandlers>;
13
18
  export {};
14
19
  //# sourceMappingURL=rpcBoundaryHandlers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rpcBoundaryHandlers.d.ts","sourceRoot":"","sources":["../src/rpcBoundaryHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,GAAG,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG3D,KAAK,QAAQ,CAAC,CAAC,SAAS,GAAG,CAAC,GAAG,IAAI;IAClC,QAAQ,EAAE,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CACrC,GAAG,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,KACtC,GAAG,CAAC,SAAS,CACjB,IAAI,SAAS;QAAE,QAAQ,CAAC,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAA;KAAE,GAChF,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GACjD,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CACrD;CACD,CAAC;AAEF,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,IAChB,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC,GAChD,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GACtB,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC,GAClD,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GACtB,CAAC,SAAS,GAAG,CAAC,OAAO,CAAC,MAAM,MAAM,CAAC,GAClC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAC/B,KAAK,CAAC;AAEZ,KAAK,eAAe,CAAC,SAAS,EAAE,CAAC,IAAI;IACpC,QAAQ,EAAE,IAAI,IAAI,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,MAAM,KAAK,KAAK,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,KAAK;CACnJ,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,SAAS,SAAS,QAAQ,CAAC,KAAK,CAAC,EAC9F,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAC/B,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EACvF,QAAQ,EAAE,SAAS,GACjB,eAAe,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"rpcBoundaryHandlers.d.ts","sourceRoot":"","sources":["../src/rpcBoundaryHandlers.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,24 @@
1
- import { Effect, Result, Stream } from "effect";
1
+ import { Effect, Result, Schema, Stream } from "effect";
2
2
  import { Rpc, RpcSchema } from "effect/rpc";
3
- export function rpcBoundaryHandlers(group, boundary, handlers) {
4
- return Object.fromEntries(Object.entries(handlers).map(([tag, handler]) => [
5
- tag,
6
- (...args) => {
7
- const constructed = Result.try(() => handler(...args));
8
- if (Result.isFailure(constructed)) {
9
- const failure = boundary(Effect.die(constructed.failure));
10
- const rpc = group.requests.get(tag);
11
- return Rpc.isRpc(rpc) && RpcSchema.isStreamSchema(rpc.successSchema) ? Stream.fromEffect(failure) : failure;
12
- }
13
- return Rpc.wrapMap(constructed.success, (result) => Effect.isEffect(result) ? boundary(result) : Stream.catchCause(result, (cause) => Stream.fromEffect(boundary(Effect.failCause(cause)))));
14
- },
15
- ]));
3
+ import { rpcBoundary } from "#boundary.ts";
4
+ export function rpcBoundaryHandlers(group, options, handlers) {
5
+ return Object.fromEntries(Array.from(group.requests.values(), (rpc) => {
6
+ const tag = rpc._tag;
7
+ const handler = handlers[tag];
8
+ const streaming = RpcSchema.isStreamSchema(rpc.successSchema);
9
+ const schema = RpcSchema.isStreamSchema(rpc.successSchema) ? Schema.Union([rpc.errorSchema, rpc.successSchema.error]) : rpc.errorSchema;
10
+ const boundary = rpcBoundary(schema, options);
11
+ return [
12
+ tag,
13
+ (...args) => {
14
+ const constructed = Result.try(() => handler(...args));
15
+ if (Result.isFailure(constructed)) {
16
+ const failure = boundary(Effect.die(constructed.failure));
17
+ return streaming ? Stream.fromEffect(failure) : failure;
18
+ }
19
+ return Rpc.wrapMap(constructed.success, (result) => Effect.isEffect(result) ? boundary(result) : Stream.catchCause(result, (cause) => Stream.fromEffect(boundary(Effect.failCause(cause)))));
20
+ },
21
+ ];
22
+ }));
16
23
  }
17
24
  //# sourceMappingURL=rpcBoundaryHandlers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rpcBoundaryHandlers.js","sourceRoot":"","sources":["../src/rpcBoundaryHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,GAAG,EAAiB,SAAS,EAAE,MAAM,YAAY,CAAC;AA+B3D,MAAM,UAAU,mBAAmB,CAClC,KAAiC,EACjC,QAA6F,EAC7F,QAAsF;IAEtF,OAAO,MAAM,CAAC,WAAW,CACxB,MAAM,CAAC,OAAO,CAAqD,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;QACpG,GAAG;QACH,CAAC,GAAG,IAAa,EAAE,EAAE;YACpB,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YACvD,IAAI,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC;gBACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;gBAC1D,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACpC,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,cAAc,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAC7G,CAAC;YACD,OAAO,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAClD,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CACvI,CAAC;QACH,CAAC;KACD,CAAC,CACF,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"rpcBoundaryHandlers.js","sourceRoot":"","sources":["../src/rpcBoundaryHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AACpE,OAAO,EAAE,GAAG,EAAiB,SAAS,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAA2B,WAAW,EAAE,MAAM,cAAc,CAAC;AAuCpE,MAAM,UAAU,mBAAmB,CAClC,KAAoE,EACpE,OAAoC,EACpC,QAAoF;IAEpF,OAAO,MAAM,CAAC,WAAW,CACxB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;QAC3C,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC;QACrB,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,SAAS,GAAG,SAAS,CAAC,cAAc,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,SAAS,CAAC,cAAc,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC;QACxI,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO;YACN,GAAG;YACH,CAAC,GAAG,IAAa,EAAE,EAAE;gBACpB,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;gBACvD,IAAI,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC;oBACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;oBAC1D,OAAO,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;gBACzD,CAAC;gBACD,OAAO,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAClD,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CACvI,CAAC;YACH,CAAC;SACD,CAAC;IACH,CAAC,CAAC,CACF,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/errors",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Typed expected failures and secret-safe failure reporting for Effect applications",
5
5
  "keywords": [
6
6
  "effect"
@@ -45,8 +45,8 @@
45
45
  "README.md"
46
46
  ],
47
47
  "devDependencies": {
48
- "@shivaedev/quality": "0.57.0",
49
- "@shivaedev/test-story": "0.35.0",
48
+ "@shivaedev/quality": "0.57.1",
49
+ "@shivaedev/test-story": "0.36.0",
50
50
  "@types/node": "24.19.2",
51
51
  "effect": "4.0.6",
52
52
  "typescript": "7.0.2",
package/src/boundary.ts CHANGED
@@ -36,11 +36,7 @@ export interface RpcBoundaryOptions<E> {
36
36
  readonly message?: string;
37
37
  readonly unexpected: (message: string) => E;
38
38
  }
39
- export function rpcBoundary<
40
- TSchema extends Schema.ConstraintDecoder<{
41
- readonly message: string;
42
- }>,
43
- >(schema: TSchema, options: RpcBoundaryOptions<TSchema["Type"]>) {
39
+ export function rpcBoundary<TSchema extends Schema.Constraint>(schema: TSchema, options: RpcBoundaryOptions<TSchema["Type"]>) {
44
40
  const declared = Schema.is(schema);
45
41
  return <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, TSchema["Type"], R> =>
46
42
  Effect.catchCause(self, (cause) => {
@@ -1,5 +1,6 @@
1
- import { Effect, Result, Stream } from "effect";
1
+ import { Effect, Result, Schema, Stream, type Types } from "effect";
2
2
  import { Rpc, type RpcGroup, RpcSchema } from "effect/rpc";
3
+ import { type RpcBoundaryOptions, rpcBoundary } from "#boundary.ts";
3
4
 
4
5
  type HandlerResult = Effect.Effect<unknown, unknown, unknown> | Stream.Stream<unknown, unknown, unknown>;
5
6
  type Handlers<R extends Rpc.Any> = {
@@ -21,34 +22,48 @@ type Bounded<T, E> =
21
22
  ? Rpc.Wrapper<Bounded<TValue, E>>
22
23
  : never;
23
24
 
24
- type BoundedHandlers<THandlers, E> = {
25
- readonly [TKey in keyof THandlers]: THandlers[TKey] extends (...args: infer TArgs) => infer TValue ? (...args: TArgs) => Bounded<TValue, E> : never;
25
+ type Rejection<TRpc extends Rpc.Any> = TRpc extends Rpc.AnyWithProps
26
+ ? TRpc["errorSchema"]["Type"] | (TRpc["successSchema"] extends RpcSchema.Stream<infer _A, infer E> ? E["Type"] : never)
27
+ : never;
28
+
29
+ type BoundedHandlers<TRpcs extends Rpc.Any, THandlers> = {
30
+ readonly [TKey in keyof THandlers]: THandlers[TKey] extends (...args: infer TArgs) => infer TValue
31
+ ? (...args: TArgs) => Bounded<TValue, Rejection<Extract<TRpcs, { readonly _tag: TKey }>>>
32
+ : never;
26
33
  };
27
34
 
28
- export function rpcBoundaryHandlers<TRpcs extends Rpc.Any, E, THandlers extends Handlers<TRpcs>>(
35
+ type GroupOptions<TRpcs extends Rpc.Any> = Types.UnionToIntersection<TRpcs extends unknown ? RpcBoundaryOptions<Rejection<TRpcs>> : never>;
36
+
37
+ export function rpcBoundaryHandlers<TRpcs extends Rpc.Any, THandlers extends Handlers<TRpcs>>(
29
38
  group: RpcGroup.RpcGroup<TRpcs>,
30
- boundary: <A, TError, R>(effect: Effect.Effect<A, TError, R>) => Effect.Effect<A, E, R>,
39
+ options: NoInfer<GroupOptions<TRpcs>>,
31
40
  handlers: THandlers,
32
- ): BoundedHandlers<THandlers, E>;
33
- export function rpcBoundaryHandlers(
34
- group: RpcGroup.RpcGroup<Rpc.Any>,
35
- boundary: <A, TError, R>(effect: Effect.Effect<A, TError, R>) => Effect.Effect<A, unknown, R>,
36
- handlers: Readonly<Record<string, (...args: never[]) => Rpc.WrapperOr<HandlerResult>>>,
41
+ ): BoundedHandlers<TRpcs, THandlers>;
42
+ export function rpcBoundaryHandlers<TTag extends string>(
43
+ group: RpcGroup.RpcGroup<Rpc.AnyWithProps & { readonly _tag: TTag }>,
44
+ options: RpcBoundaryOptions<unknown>,
45
+ handlers: Readonly<Record<TTag, (...args: never[]) => Rpc.WrapperOr<HandlerResult>>>,
37
46
  ) {
38
47
  return Object.fromEntries(
39
- Object.entries<(...args: never[]) => Rpc.WrapperOr<HandlerResult>>(handlers).map(([tag, handler]) => [
40
- tag,
41
- (...args: never[]) => {
42
- const constructed = Result.try(() => handler(...args));
43
- if (Result.isFailure(constructed)) {
44
- const failure = boundary(Effect.die(constructed.failure));
45
- const rpc = group.requests.get(tag);
46
- return Rpc.isRpc(rpc) && RpcSchema.isStreamSchema(rpc.successSchema) ? Stream.fromEffect(failure) : failure;
47
- }
48
- return Rpc.wrapMap(constructed.success, (result) =>
49
- Effect.isEffect(result) ? boundary(result) : Stream.catchCause(result, (cause) => Stream.fromEffect(boundary(Effect.failCause(cause)))),
50
- );
51
- },
52
- ]),
48
+ Array.from(group.requests.values(), (rpc) => {
49
+ const tag = rpc._tag;
50
+ const handler = handlers[tag];
51
+ const streaming = RpcSchema.isStreamSchema(rpc.successSchema);
52
+ const schema = RpcSchema.isStreamSchema(rpc.successSchema) ? Schema.Union([rpc.errorSchema, rpc.successSchema.error]) : rpc.errorSchema;
53
+ const boundary = rpcBoundary(schema, options);
54
+ return [
55
+ tag,
56
+ (...args: never[]) => {
57
+ const constructed = Result.try(() => handler(...args));
58
+ if (Result.isFailure(constructed)) {
59
+ const failure = boundary(Effect.die(constructed.failure));
60
+ return streaming ? Stream.fromEffect(failure) : failure;
61
+ }
62
+ return Rpc.wrapMap(constructed.success, (result) =>
63
+ Effect.isEffect(result) ? boundary(result) : Stream.catchCause(result, (cause) => Stream.fromEffect(boundary(Effect.failCause(cause)))),
64
+ );
65
+ },
66
+ ];
67
+ }),
53
68
  );
54
69
  }