@orpc/experimental-effect 2.0.0-beta.4 → 2.0.0-beta.41

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.
@@ -4,41 +4,41 @@ import { Schema } from 'effect';
4
4
 
5
5
  declare module '@orpc/contract' {
6
6
  interface ContractBuilder<TErrorMap extends ErrorMap> {
7
- input<A, I>(schema: Schema.Schema<A, I>): ProcedureContractBuilderWithInput<Schema$1<I, A>, TErrorMap>;
8
- output<A, I>(schema: Schema.Schema<A, I>): ProcedureContractBuilderWithOutput<Schema$1<I, A>, TErrorMap>;
7
+ input<S extends Schema.ConstraintDecoder<any>>(schema: S): ProcedureContractBuilderWithInput<Schema$1<S['Encoded'], S['Type']>, TErrorMap>;
8
+ output<S extends Schema.ConstraintDecoder<any>>(schema: S): ProcedureContractBuilderWithOutput<Schema$1<S['Encoded'], S['Type']>, TErrorMap>;
9
9
  }
10
10
  interface ProcedureContractBuilderWithInput<TInputSchema extends AnySchema, TErrorMap extends ErrorMap> {
11
- input<A, I>(schema: Schema.Schema<A, I>): ProcedureContractBuilderWithInput<MergedSchema<Schema$1<I, A>, TInputSchema>, TErrorMap>;
12
- output<A, I>(schema: Schema.Schema<A, I>): ProcedureContractBuilderWithInputOutput<TInputSchema, Schema$1<I, A>, TErrorMap>;
11
+ input<S extends Schema.ConstraintDecoder<any>>(schema: S): ProcedureContractBuilderWithInput<MergedSchema<Schema$1<S['Encoded'], S['Type']>, TInputSchema>, TErrorMap>;
12
+ output<S extends Schema.ConstraintDecoder<any>>(schema: S): ProcedureContractBuilderWithInputOutput<TInputSchema, Schema$1<S['Encoded'], S['Type']>, TErrorMap>;
13
13
  }
14
14
  interface ProcedureContractBuilderWithOutput<TOutputSchema extends AnySchema, TErrorMap extends ErrorMap> {
15
- input<A, I>(schema: Schema.Schema<A, I>): ProcedureContractBuilderWithInputOutput<Schema$1<I, A>, TOutputSchema, TErrorMap>;
16
- output<A, I>(schema: Schema.Schema<A, I>): ProcedureContractBuilderWithOutput<MergedSchema<Schema$1<I, A>, TOutputSchema>, TErrorMap>;
15
+ input<S extends Schema.ConstraintDecoder<any>>(schema: S): ProcedureContractBuilderWithInputOutput<Schema$1<S['Encoded'], S['Type']>, TOutputSchema, TErrorMap>;
16
+ output<S extends Schema.ConstraintDecoder<any>>(schema: S): ProcedureContractBuilderWithOutput<MergedSchema<Schema$1<S['Encoded'], S['Type']>, TOutputSchema>, TErrorMap>;
17
17
  }
18
18
  interface ProcedureContractBuilderWithInputOutput<TInputSchema extends AnySchema, TOutputSchema extends AnySchema, TErrorMap extends ErrorMap> {
19
- input<A, I>(schema: Schema.Schema<A, I>): ProcedureContractBuilderWithInputOutput<MergedSchema<Schema$1<I, A>, TInputSchema>, TOutputSchema, TErrorMap>;
20
- output<A, I>(schema: Schema.Schema<A, I>): ProcedureContractBuilderWithInputOutput<TInputSchema, MergedSchema<Schema$1<I, A>, TOutputSchema>, TErrorMap>;
19
+ input<S extends Schema.ConstraintDecoder<any>>(schema: S): ProcedureContractBuilderWithInputOutput<MergedSchema<Schema$1<S['Encoded'], S['Type']>, TInputSchema>, TOutputSchema, TErrorMap>;
20
+ output<S extends Schema.ConstraintDecoder<any>>(schema: S): ProcedureContractBuilderWithInputOutput<TInputSchema, MergedSchema<Schema$1<S['Encoded'], S['Type']>, TOutputSchema>, TErrorMap>;
21
21
  }
22
22
  }
23
23
  declare module '@orpc/server' {
24
24
  interface Builder<TInitialContext extends Context, TErrorMap extends ErrorMap> {
25
- input<A, I>(schema: Schema.Schema<A, I>): BuilderWithInput<TInitialContext, object, Schema$1<I, A>, TErrorMap>;
26
- output<A, I>(schema: Schema.Schema<A, I>): BuilderWithOutput<TInitialContext, object, Schema$1<I, A>, TErrorMap>;
25
+ input<S extends Schema.ConstraintDecoder<any>>(schema: S): BuilderWithInput<TInitialContext, object, Schema$1<S['Encoded'], S['Type']>, TErrorMap>;
26
+ output<S extends Schema.ConstraintDecoder<any>>(schema: S): BuilderWithOutput<TInitialContext, object, Schema$1<S['Encoded'], S['Type']>, TErrorMap>;
27
27
  }
28
28
  interface BuilderWithMiddlewares<TInitialContext extends Context, TInjectedContext extends Context, TErrorMap extends ErrorMap> {
29
- input<A, I>(schema: Schema.Schema<A, I>): BuilderWithInput<TInitialContext, TInjectedContext, Schema$1<I, A>, TErrorMap>;
30
- output<A, I>(schema: Schema.Schema<A, I>): BuilderWithOutput<TInitialContext, TInjectedContext, Schema$1<I, A>, TErrorMap>;
29
+ input<S extends Schema.ConstraintDecoder<any>>(schema: S): BuilderWithInput<TInitialContext, TInjectedContext, Schema$1<S['Encoded'], S['Type']>, TErrorMap>;
30
+ output<S extends Schema.ConstraintDecoder<any>>(schema: S): BuilderWithOutput<TInitialContext, TInjectedContext, Schema$1<S['Encoded'], S['Type']>, TErrorMap>;
31
31
  }
32
32
  interface BuilderWithInput<TInitialContext extends Context, TInjectedContext extends Context, TInputSchema extends AnySchema, TErrorMap extends ErrorMap> {
33
- input<A, I>(schema: Schema.Schema<A, I>): BuilderWithInput<TInitialContext, TInjectedContext, MergedSchema<Schema$1<I, A>, TInputSchema>, TErrorMap>;
34
- output<A, I>(schema: Schema.Schema<A, I>): BuilderWithInputOutput<TInitialContext, TInjectedContext, TInputSchema, Schema$1<I, A>, TErrorMap>;
33
+ input<S extends Schema.ConstraintDecoder<any>>(schema: S): BuilderWithInput<TInitialContext, TInjectedContext, MergedSchema<Schema$1<S['Encoded'], S['Type']>, TInputSchema>, TErrorMap>;
34
+ output<S extends Schema.ConstraintDecoder<any>>(schema: S): BuilderWithInputOutput<TInitialContext, TInjectedContext, TInputSchema, Schema$1<S['Encoded'], S['Type']>, TErrorMap>;
35
35
  }
36
36
  interface BuilderWithOutput<TInitialContext extends Context, TInjectedContext extends Context, TOutputSchema extends AnySchema, TErrorMap extends ErrorMap> {
37
- input<A, I>(schema: Schema.Schema<A, I>): BuilderWithInputOutput<TInitialContext, TInjectedContext, Schema$1<I, A>, TOutputSchema, TErrorMap>;
38
- output<A, I>(schema: Schema.Schema<A, I>): BuilderWithOutput<TInitialContext, TInjectedContext, MergedSchema<Schema$1<I, A>, TOutputSchema>, TErrorMap>;
37
+ input<S extends Schema.ConstraintDecoder<any>>(schema: S): BuilderWithInputOutput<TInitialContext, TInjectedContext, Schema$1<S['Encoded'], S['Type']>, TOutputSchema, TErrorMap>;
38
+ output<S extends Schema.ConstraintDecoder<any>>(schema: S): BuilderWithOutput<TInitialContext, TInjectedContext, MergedSchema<Schema$1<S['Encoded'], S['Type']>, TOutputSchema>, TErrorMap>;
39
39
  }
40
40
  interface BuilderWithInputOutput<TInitialContext extends Context, TInjectedContext extends Context, TInputSchema extends AnySchema, TOutputSchema extends AnySchema, TErrorMap extends ErrorMap> {
41
- input<A, I>(schema: Schema.Schema<A, I>): BuilderWithInputOutput<TInitialContext, TInjectedContext, MergedSchema<Schema$1<I, A>, TInputSchema>, TOutputSchema, TErrorMap>;
42
- output<A, I>(schema: Schema.Schema<A, I>): BuilderWithInputOutput<TInitialContext, TInjectedContext, TInputSchema, MergedSchema<Schema$1<I, A>, TOutputSchema>, TErrorMap>;
41
+ input<S extends Schema.ConstraintDecoder<any>>(schema: S): BuilderWithInputOutput<TInitialContext, TInjectedContext, MergedSchema<Schema$1<S['Encoded'], S['Type']>, TInputSchema>, TOutputSchema, TErrorMap>;
42
+ output<S extends Schema.ConstraintDecoder<any>>(schema: S): BuilderWithInputOutput<TInitialContext, TInjectedContext, TInputSchema, MergedSchema<Schema$1<S['Encoded'], S['Type']>, TOutputSchema>, TErrorMap>;
43
43
  }
44
44
  }
@@ -1,21 +1,24 @@
1
1
  import { ContractBuilder } from '@orpc/contract';
2
2
  import { Builder } from '@orpc/server';
3
3
  import { Schema } from 'effect';
4
- import { t as toStandardSchema } from '../shared/experimental-effect.B68ZlIzV.mjs';
4
+ import { t as toStandardSchema } from '../shared/experimental-effect.YQKompVf.mjs';
5
5
 
6
+ function isEffectConstraintDecoder(schema) {
7
+ return Schema.isSchema(schema);
8
+ }
6
9
  const OriginalContractBuilderInput = ContractBuilder.prototype.input;
7
10
  ContractBuilder.prototype.input = function input(schema) {
8
- return OriginalContractBuilderInput.bind(this)(Schema.isSchema(schema) ? toStandardSchema(schema) : schema);
11
+ return OriginalContractBuilderInput.bind(this)(isEffectConstraintDecoder(schema) ? toStandardSchema(schema) : schema);
9
12
  };
10
13
  const OriginalContractBuilderOutput = ContractBuilder.prototype.output;
11
14
  ContractBuilder.prototype.output = function output(schema) {
12
- return OriginalContractBuilderOutput.bind(this)(Schema.isSchema(schema) ? toStandardSchema(schema) : schema);
15
+ return OriginalContractBuilderOutput.bind(this)(isEffectConstraintDecoder(schema) ? toStandardSchema(schema) : schema);
13
16
  };
14
17
  const OriginalBuilderInput = Builder.prototype.input;
15
18
  Builder.prototype.input = function input2(schema) {
16
- return OriginalBuilderInput.bind(this)(Schema.isSchema(schema) ? toStandardSchema(schema) : schema);
19
+ return OriginalBuilderInput.bind(this)(isEffectConstraintDecoder(schema) ? toStandardSchema(schema) : schema);
17
20
  };
18
21
  const OriginalBuilderOutput = Builder.prototype.output;
19
22
  Builder.prototype.output = function output2(schema) {
20
- return OriginalBuilderOutput.bind(this)(Schema.isSchema(schema) ? toStandardSchema(schema) : schema);
23
+ return OriginalBuilderOutput.bind(this)(isEffectConstraintDecoder(schema) ? toStandardSchema(schema) : schema);
21
24
  };
package/dist/index.d.mts CHANGED
@@ -1,30 +1,172 @@
1
- export { H as HandlerGen, I as InferYieldError, W as WithEffectContext, h as handlerGen } from './shared/experimental-effect.CuTs7Ks0.mjs';
2
- import { AnySchema, Schema as Schema$1 } from '@orpc/contract';
1
+ import { AnyNestedClient, Client, ClientRest } from '@orpc/client';
2
+ import { Effect, Schema } from 'effect';
3
+ import { I as InferEffectServices } from './shared/experimental-effect.D0zFxpKr.mjs';
4
+ export { H as HandlerGen, a as InferYieldError, W as WithEffectContext, h as handlerGen } from './shared/experimental-effect.D0zFxpKr.mjs';
5
+ import { AnySchema, ErrorMap, Schema as Schema$1 } from '@orpc/contract';
3
6
  import { JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchema } from '@orpc/json-schema';
4
- import { Cause, Effect, Schema } from 'effect';
5
- import '@orpc/server';
6
- import 'effect/Utils';
7
+ import { AnyORPCError, ORPCError, ORPCErrorCode, Context, ORPCErrorConstructorMap, MiddlewareOptions, MiddlewareNextOptions, MiddlewareResult, MiddlewareDone, Middleware } from '@orpc/server';
8
+ import { MaybeOptionalOptions } from '@orpc/shared';
7
9
 
10
+ type EffectClient<T extends AnyNestedClient> = T extends Client<infer UContext, infer UInput, infer UOutput, infer UError> ? (...rest: ClientRest<UContext, UInput>) => Effect.Effect<UOutput, UError> : {
11
+ [K in keyof T]: T[K] extends AnyNestedClient ? EffectClient<T[K]> : never;
12
+ };
13
+ /**
14
+ * Creates a client whose procedures return lazy effects instead of promises,
15
+ * so you can `yield*` calls inside Effect generators. Errors are captured in
16
+ * the error channel with their original types preserved, and interrupting an
17
+ * effect aborts the underlying call.
18
+ *
19
+ * @see {@link https://orpc.dev/docs/integrations/effect#client-calls | Effect Integration - Client Calls}
20
+ */
21
+ declare function createEffectClient<T extends AnyNestedClient>(client: T): EffectClient<T>;
22
+
23
+ /**
24
+ * A JSON Schema converter for Effect Schema, built on top of
25
+ * [Effect Schema to JSON Schema](https://effect.website/docs/schema/json-schema/).
26
+ * Useful with tools such as the OpenAPI Generator.
27
+ *
28
+ * @see {@link https://orpc.dev/docs/integrations/effect#json-schema-converter | Effect Integration - JSON Schema Converter}
29
+ */
8
30
  declare class EffectSchemaToJsonSchemaConverter implements JsonSchemaConverter {
31
+ private readonly converter;
9
32
  condition(schema: AnySchema | undefined, _direction: JsonSchemaConverterDirection): boolean;
10
33
  convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
11
34
  }
12
35
 
13
36
  /**
14
- * Extracts the most meaningful original error from an Effect Cause,
15
- * preserving the original error instance wherever possible.
37
+ * Recovers from `ORPCError` failures in the error channel of an effect.
38
+ * Other failures re-fail with their original cause. Because failure types
39
+ * like `Error` can also hold `ORPCError` instances at runtime, the handler
40
+ * can also receive such hidden errors with unknown code and data.
41
+ *
42
+ * Supports both data-first `catchORPCError(effect, handler)` and data-last
43
+ * `effect.pipe(catchORPCError(handler))` styles.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * declare const program: Effect.Effect<string, ORPCError<'NOT_FOUND', undefined> | TypeError>
48
+ *
49
+ * const recovered = program.pipe(
50
+ * catchORPCError(error => Effect.succeed(`caught ${error.code}`)),
51
+ * )
52
+ * ```
53
+ *
54
+ * @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
55
+ */
56
+ declare const catchORPCError: {
57
+ <E, A2, E2, R2>(f: (error: E extends AnyORPCError ? E : AnyORPCError extends E ? ORPCError<ORPCErrorCode, unknown> : never) => Effect.Effect<A2, E2, R2>): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A | A2, E2 | Exclude<E, AnyORPCError>, R | R2>;
58
+ <A, E, R, A2, E2, R2>(self: Effect.Effect<A, E, R>, f: (error: E extends AnyORPCError ? E : AnyORPCError extends E ? ORPCError<ORPCErrorCode, unknown> : never) => Effect.Effect<A2, E2, R2>): Effect.Effect<A | A2, E2 | Exclude<E, AnyORPCError>, R | R2>;
59
+ };
60
+ /**
61
+ * Recovers from `ORPCError` failures with a matching code in the error channel of an effect.
62
+ * Other failures re-fail with their original cause. The `code` argument is
63
+ * suggested and restricted to the codes present in the error channel, and is
64
+ * unrestricted when the channel contains failure types like `Error` that can
65
+ * hold hidden `ORPCError` instances, whose data is unknown.
66
+ *
67
+ * Supports both data-first `catchORPCErrorCode(effect, code, handler)` and
68
+ * data-last `effect.pipe(catchORPCErrorCode(code, handler))` styles.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * declare const program: Effect.Effect<
73
+ * string,
74
+ * ORPCError<'NOT_FOUND', { id: string }> | ORPCError<'CONFLICT', undefined>
75
+ * >
76
+ *
77
+ * const recovered = program.pipe(
78
+ * catchORPCErrorCode('NOT_FOUND', error => Effect.succeed(error.data.id)),
79
+ * )
80
+ * ```
81
+ *
82
+ * @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
83
+ */
84
+ declare const catchORPCErrorCode: {
85
+ <const TCode extends E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never, E, A2, E2, R2>(code: TCode, f: (error: E extends AnyORPCError ? Extract<E, ORPCError<TCode, any>> : AnyORPCError extends E ? ORPCError<TCode, unknown> : never) => Effect.Effect<A2, E2, R2>): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A | A2, E2 | Exclude<E, ORPCError<TCode, any>>, R | R2>;
86
+ <A, E, R, const TCode extends E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never, A2, E2, R2>(self: Effect.Effect<A, E, R>, code: TCode, f: (error: E extends AnyORPCError ? Extract<E, ORPCError<TCode, any>> : AnyORPCError extends E ? ORPCError<TCode, unknown> : never) => Effect.Effect<A2, E2, R2>): Effect.Effect<A | A2, E2 | Exclude<E, ORPCError<TCode, any>>, R | R2>;
87
+ };
88
+ /**
89
+ * Recovers from `ORPCError` failures using a map of codes to handlers in the
90
+ * error channel of an effect. Other failures re-fail with their original
91
+ * cause. The keys are suggested and restricted to the codes present in the
92
+ * error channel, and are unrestricted when the channel contains failure types
93
+ * like `Error` that can hold hidden `ORPCError` instances, whose data is unknown.
94
+ *
95
+ * Supports both data-first `catchORPCErrorCodes(effect, cases)` and data-last
96
+ * `effect.pipe(catchORPCErrorCodes(cases))` styles.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * declare const program: Effect.Effect<
101
+ * string,
102
+ * ORPCError<'NOT_FOUND', { id: string }> | ORPCError<'CONFLICT', undefined>
103
+ * >
104
+ *
105
+ * const recovered = program.pipe(
106
+ * catchORPCErrorCodes({
107
+ * NOT_FOUND: error => Effect.succeed(error.data.id),
108
+ * CONFLICT: error => Effect.succeed(error.message),
109
+ * }),
110
+ * )
111
+ * ```
112
+ *
113
+ * @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
114
+ */
115
+ declare const catchORPCErrorCodes: {
116
+ <E, Cases extends {
117
+ [K in E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never]?: string extends K ? (error: never) => Effect.Effect<any, any, any> : K extends infer C extends ORPCErrorCode ? (error: E extends AnyORPCError ? Extract<E, ORPCError<C, any>> : AnyORPCError extends E ? ORPCError<C, unknown> : never) => Effect.Effect<any, any, any> : never;
118
+ } & {
119
+ [K in Exclude<keyof Cases, E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never>]: never;
120
+ }>(cases: Cases): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A | {
121
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<infer A2, any, any> ? A2 : never;
122
+ }[keyof Cases], Exclude<E, ORPCError<keyof Cases & ORPCErrorCode, any>> | {
123
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, infer E2, any> ? E2 : never;
124
+ }[keyof Cases], R | {
125
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, any, infer R2> ? R2 : never;
126
+ }[keyof Cases]>;
127
+ <A, E, R, Cases extends {
128
+ [K in E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never]?: string extends K ? (error: never) => Effect.Effect<any, any, any> : K extends infer C extends ORPCErrorCode ? (error: E extends AnyORPCError ? Extract<E, ORPCError<C, any>> : AnyORPCError extends E ? ORPCError<C, unknown> : never) => Effect.Effect<any, any, any> : never;
129
+ } & {
130
+ [K in Exclude<keyof Cases, E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never>]: never;
131
+ }>(self: Effect.Effect<A, E, R>, cases: Cases): Effect.Effect<A | {
132
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<infer A2, any, any> ? A2 : never;
133
+ }[keyof Cases], Exclude<E, ORPCError<keyof Cases & ORPCErrorCode, any>> | {
134
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, infer E2, any> ? E2 : never;
135
+ }[keyof Cases], R | {
136
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, any, infer R2> ? R2 : never;
137
+ }[keyof Cases]>;
138
+ };
139
+
140
+ interface MiddlewareGenNext<TOutput> {
141
+ <U extends Context = object>(...rest: MaybeOptionalOptions<MiddlewareNextOptions<U>>): Effect.Effect<Awaited<MiddlewareResult<U, TOutput>>, unknown>;
142
+ }
143
+ interface MiddlewareGenOptions<TInContext extends Context, TOutput, TErrorConstructorMap extends ORPCErrorConstructorMap<any>> extends Omit<MiddlewareOptions<TInContext, TOutput, TErrorConstructorMap>, 'next'> {
144
+ next: MiddlewareGenNext<TOutput>;
145
+ }
146
+ interface MiddlewareGen<TInContext extends Context, TOutContext extends Context, TInput, TOutput, TYield extends Effect.Effect<any, any, InferEffectServices<TInContext>>, TErrorConstructorMap extends ORPCErrorConstructorMap<any>> {
147
+ (opts: MiddlewareGenOptions<TInContext, TOutput, TErrorConstructorMap>, input: TInput, done: MiddlewareDone<TOutput>): Generator<TYield, MiddlewareResult<TOutContext, TOutput>, never>;
148
+ }
149
+ type AnyMiddlewareGen = MiddlewareGen<any, any, any, any, any, any>;
150
+ /**
151
+ * Creates an oRPC middleware from an Effect generator function.
152
+ * Inside the generator you can yield Effect operations and `yield* next()`
153
+ * to continue the chain, and `middlewareGen` handles the execution and
154
+ * error handling for you.
155
+ *
156
+ * @see {@link https://orpc.dev/docs/integrations/effect#effectful-middleware | Effect Integration - Effectful Middleware}
16
157
  */
17
- declare function extractErrorFromCause(cause: Cause.Cause<unknown>): unknown;
158
+ declare function middlewareGen<TInContext extends Context, TOutContext extends Context = object, TInput = unknown, TOutput = any, // TOutput = any by default is important to make middleware can be used in any output by default
159
+ TErrorMap extends ErrorMap = Record<never, never>, TYield extends Effect.Effect<any, any, InferEffectServices<TInContext>> = Effect.Effect<any, any, InferEffectServices<TInContext>>>(middleware: MiddlewareGen<TInContext, TOutContext, TInput, TOutput, TYield, ORPCErrorConstructorMap<TErrorMap>>): Middleware<TInContext, TOutContext, TInput, TOutput, TErrorMap>;
160
+
18
161
  interface RunPromiseOptions {
19
162
  signal?: undefined | AbortSignal;
20
163
  }
21
164
  /**
22
- * Runs an Effect as a Promise while re-throwing the original error directly,
23
- * bypassing Effect.runPromise's FiberFailure wrapper.
165
+ * Runs an Effect as a Promise and throws the most meaningful errors.
24
166
  */
25
167
  declare function runPromise<T>(effect: Effect.Effect<T, unknown>, options?: RunPromiseOptions): Promise<T>;
26
168
 
27
- declare function toStandardSchema<A, I>(schema: Schema.Schema<A, I>): Schema$1<I, A>;
169
+ declare function toStandardSchema<S extends Schema.ConstraintDecoder<any>>(schema: S): Schema$1<S['Encoded'], S['Type']> & S;
28
170
 
29
- export { EffectSchemaToJsonSchemaConverter, extractErrorFromCause, runPromise, toStandardSchema };
30
- export type { RunPromiseOptions };
171
+ export { EffectSchemaToJsonSchemaConverter, InferEffectServices, catchORPCError, catchORPCErrorCode, catchORPCErrorCodes, createEffectClient, middlewareGen, runPromise, toStandardSchema };
172
+ export type { AnyMiddlewareGen, EffectClient, MiddlewareGen, MiddlewareGenNext, MiddlewareGenOptions, RunPromiseOptions };
package/dist/index.d.ts CHANGED
@@ -1,30 +1,172 @@
1
- export { H as HandlerGen, I as InferYieldError, W as WithEffectContext, h as handlerGen } from './shared/experimental-effect.CuTs7Ks0.js';
2
- import { AnySchema, Schema as Schema$1 } from '@orpc/contract';
1
+ import { AnyNestedClient, Client, ClientRest } from '@orpc/client';
2
+ import { Effect, Schema } from 'effect';
3
+ import { I as InferEffectServices } from './shared/experimental-effect.D0zFxpKr.js';
4
+ export { H as HandlerGen, a as InferYieldError, W as WithEffectContext, h as handlerGen } from './shared/experimental-effect.D0zFxpKr.js';
5
+ import { AnySchema, ErrorMap, Schema as Schema$1 } from '@orpc/contract';
3
6
  import { JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchema } from '@orpc/json-schema';
4
- import { Cause, Effect, Schema } from 'effect';
5
- import '@orpc/server';
6
- import 'effect/Utils';
7
+ import { AnyORPCError, ORPCError, ORPCErrorCode, Context, ORPCErrorConstructorMap, MiddlewareOptions, MiddlewareNextOptions, MiddlewareResult, MiddlewareDone, Middleware } from '@orpc/server';
8
+ import { MaybeOptionalOptions } from '@orpc/shared';
7
9
 
10
+ type EffectClient<T extends AnyNestedClient> = T extends Client<infer UContext, infer UInput, infer UOutput, infer UError> ? (...rest: ClientRest<UContext, UInput>) => Effect.Effect<UOutput, UError> : {
11
+ [K in keyof T]: T[K] extends AnyNestedClient ? EffectClient<T[K]> : never;
12
+ };
13
+ /**
14
+ * Creates a client whose procedures return lazy effects instead of promises,
15
+ * so you can `yield*` calls inside Effect generators. Errors are captured in
16
+ * the error channel with their original types preserved, and interrupting an
17
+ * effect aborts the underlying call.
18
+ *
19
+ * @see {@link https://orpc.dev/docs/integrations/effect#client-calls | Effect Integration - Client Calls}
20
+ */
21
+ declare function createEffectClient<T extends AnyNestedClient>(client: T): EffectClient<T>;
22
+
23
+ /**
24
+ * A JSON Schema converter for Effect Schema, built on top of
25
+ * [Effect Schema to JSON Schema](https://effect.website/docs/schema/json-schema/).
26
+ * Useful with tools such as the OpenAPI Generator.
27
+ *
28
+ * @see {@link https://orpc.dev/docs/integrations/effect#json-schema-converter | Effect Integration - JSON Schema Converter}
29
+ */
8
30
  declare class EffectSchemaToJsonSchemaConverter implements JsonSchemaConverter {
31
+ private readonly converter;
9
32
  condition(schema: AnySchema | undefined, _direction: JsonSchemaConverterDirection): boolean;
10
33
  convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
11
34
  }
12
35
 
13
36
  /**
14
- * Extracts the most meaningful original error from an Effect Cause,
15
- * preserving the original error instance wherever possible.
37
+ * Recovers from `ORPCError` failures in the error channel of an effect.
38
+ * Other failures re-fail with their original cause. Because failure types
39
+ * like `Error` can also hold `ORPCError` instances at runtime, the handler
40
+ * can also receive such hidden errors with unknown code and data.
41
+ *
42
+ * Supports both data-first `catchORPCError(effect, handler)` and data-last
43
+ * `effect.pipe(catchORPCError(handler))` styles.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * declare const program: Effect.Effect<string, ORPCError<'NOT_FOUND', undefined> | TypeError>
48
+ *
49
+ * const recovered = program.pipe(
50
+ * catchORPCError(error => Effect.succeed(`caught ${error.code}`)),
51
+ * )
52
+ * ```
53
+ *
54
+ * @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
55
+ */
56
+ declare const catchORPCError: {
57
+ <E, A2, E2, R2>(f: (error: E extends AnyORPCError ? E : AnyORPCError extends E ? ORPCError<ORPCErrorCode, unknown> : never) => Effect.Effect<A2, E2, R2>): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A | A2, E2 | Exclude<E, AnyORPCError>, R | R2>;
58
+ <A, E, R, A2, E2, R2>(self: Effect.Effect<A, E, R>, f: (error: E extends AnyORPCError ? E : AnyORPCError extends E ? ORPCError<ORPCErrorCode, unknown> : never) => Effect.Effect<A2, E2, R2>): Effect.Effect<A | A2, E2 | Exclude<E, AnyORPCError>, R | R2>;
59
+ };
60
+ /**
61
+ * Recovers from `ORPCError` failures with a matching code in the error channel of an effect.
62
+ * Other failures re-fail with their original cause. The `code` argument is
63
+ * suggested and restricted to the codes present in the error channel, and is
64
+ * unrestricted when the channel contains failure types like `Error` that can
65
+ * hold hidden `ORPCError` instances, whose data is unknown.
66
+ *
67
+ * Supports both data-first `catchORPCErrorCode(effect, code, handler)` and
68
+ * data-last `effect.pipe(catchORPCErrorCode(code, handler))` styles.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * declare const program: Effect.Effect<
73
+ * string,
74
+ * ORPCError<'NOT_FOUND', { id: string }> | ORPCError<'CONFLICT', undefined>
75
+ * >
76
+ *
77
+ * const recovered = program.pipe(
78
+ * catchORPCErrorCode('NOT_FOUND', error => Effect.succeed(error.data.id)),
79
+ * )
80
+ * ```
81
+ *
82
+ * @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
83
+ */
84
+ declare const catchORPCErrorCode: {
85
+ <const TCode extends E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never, E, A2, E2, R2>(code: TCode, f: (error: E extends AnyORPCError ? Extract<E, ORPCError<TCode, any>> : AnyORPCError extends E ? ORPCError<TCode, unknown> : never) => Effect.Effect<A2, E2, R2>): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A | A2, E2 | Exclude<E, ORPCError<TCode, any>>, R | R2>;
86
+ <A, E, R, const TCode extends E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never, A2, E2, R2>(self: Effect.Effect<A, E, R>, code: TCode, f: (error: E extends AnyORPCError ? Extract<E, ORPCError<TCode, any>> : AnyORPCError extends E ? ORPCError<TCode, unknown> : never) => Effect.Effect<A2, E2, R2>): Effect.Effect<A | A2, E2 | Exclude<E, ORPCError<TCode, any>>, R | R2>;
87
+ };
88
+ /**
89
+ * Recovers from `ORPCError` failures using a map of codes to handlers in the
90
+ * error channel of an effect. Other failures re-fail with their original
91
+ * cause. The keys are suggested and restricted to the codes present in the
92
+ * error channel, and are unrestricted when the channel contains failure types
93
+ * like `Error` that can hold hidden `ORPCError` instances, whose data is unknown.
94
+ *
95
+ * Supports both data-first `catchORPCErrorCodes(effect, cases)` and data-last
96
+ * `effect.pipe(catchORPCErrorCodes(cases))` styles.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * declare const program: Effect.Effect<
101
+ * string,
102
+ * ORPCError<'NOT_FOUND', { id: string }> | ORPCError<'CONFLICT', undefined>
103
+ * >
104
+ *
105
+ * const recovered = program.pipe(
106
+ * catchORPCErrorCodes({
107
+ * NOT_FOUND: error => Effect.succeed(error.data.id),
108
+ * CONFLICT: error => Effect.succeed(error.message),
109
+ * }),
110
+ * )
111
+ * ```
112
+ *
113
+ * @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
114
+ */
115
+ declare const catchORPCErrorCodes: {
116
+ <E, Cases extends {
117
+ [K in E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never]?: string extends K ? (error: never) => Effect.Effect<any, any, any> : K extends infer C extends ORPCErrorCode ? (error: E extends AnyORPCError ? Extract<E, ORPCError<C, any>> : AnyORPCError extends E ? ORPCError<C, unknown> : never) => Effect.Effect<any, any, any> : never;
118
+ } & {
119
+ [K in Exclude<keyof Cases, E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never>]: never;
120
+ }>(cases: Cases): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A | {
121
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<infer A2, any, any> ? A2 : never;
122
+ }[keyof Cases], Exclude<E, ORPCError<keyof Cases & ORPCErrorCode, any>> | {
123
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, infer E2, any> ? E2 : never;
124
+ }[keyof Cases], R | {
125
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, any, infer R2> ? R2 : never;
126
+ }[keyof Cases]>;
127
+ <A, E, R, Cases extends {
128
+ [K in E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never]?: string extends K ? (error: never) => Effect.Effect<any, any, any> : K extends infer C extends ORPCErrorCode ? (error: E extends AnyORPCError ? Extract<E, ORPCError<C, any>> : AnyORPCError extends E ? ORPCError<C, unknown> : never) => Effect.Effect<any, any, any> : never;
129
+ } & {
130
+ [K in Exclude<keyof Cases, E extends ORPCError<infer TCode, any> ? TCode : AnyORPCError extends E ? ORPCErrorCode : never>]: never;
131
+ }>(self: Effect.Effect<A, E, R>, cases: Cases): Effect.Effect<A | {
132
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<infer A2, any, any> ? A2 : never;
133
+ }[keyof Cases], Exclude<E, ORPCError<keyof Cases & ORPCErrorCode, any>> | {
134
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, infer E2, any> ? E2 : never;
135
+ }[keyof Cases], R | {
136
+ [K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, any, infer R2> ? R2 : never;
137
+ }[keyof Cases]>;
138
+ };
139
+
140
+ interface MiddlewareGenNext<TOutput> {
141
+ <U extends Context = object>(...rest: MaybeOptionalOptions<MiddlewareNextOptions<U>>): Effect.Effect<Awaited<MiddlewareResult<U, TOutput>>, unknown>;
142
+ }
143
+ interface MiddlewareGenOptions<TInContext extends Context, TOutput, TErrorConstructorMap extends ORPCErrorConstructorMap<any>> extends Omit<MiddlewareOptions<TInContext, TOutput, TErrorConstructorMap>, 'next'> {
144
+ next: MiddlewareGenNext<TOutput>;
145
+ }
146
+ interface MiddlewareGen<TInContext extends Context, TOutContext extends Context, TInput, TOutput, TYield extends Effect.Effect<any, any, InferEffectServices<TInContext>>, TErrorConstructorMap extends ORPCErrorConstructorMap<any>> {
147
+ (opts: MiddlewareGenOptions<TInContext, TOutput, TErrorConstructorMap>, input: TInput, done: MiddlewareDone<TOutput>): Generator<TYield, MiddlewareResult<TOutContext, TOutput>, never>;
148
+ }
149
+ type AnyMiddlewareGen = MiddlewareGen<any, any, any, any, any, any>;
150
+ /**
151
+ * Creates an oRPC middleware from an Effect generator function.
152
+ * Inside the generator you can yield Effect operations and `yield* next()`
153
+ * to continue the chain, and `middlewareGen` handles the execution and
154
+ * error handling for you.
155
+ *
156
+ * @see {@link https://orpc.dev/docs/integrations/effect#effectful-middleware | Effect Integration - Effectful Middleware}
16
157
  */
17
- declare function extractErrorFromCause(cause: Cause.Cause<unknown>): unknown;
158
+ declare function middlewareGen<TInContext extends Context, TOutContext extends Context = object, TInput = unknown, TOutput = any, // TOutput = any by default is important to make middleware can be used in any output by default
159
+ TErrorMap extends ErrorMap = Record<never, never>, TYield extends Effect.Effect<any, any, InferEffectServices<TInContext>> = Effect.Effect<any, any, InferEffectServices<TInContext>>>(middleware: MiddlewareGen<TInContext, TOutContext, TInput, TOutput, TYield, ORPCErrorConstructorMap<TErrorMap>>): Middleware<TInContext, TOutContext, TInput, TOutput, TErrorMap>;
160
+
18
161
  interface RunPromiseOptions {
19
162
  signal?: undefined | AbortSignal;
20
163
  }
21
164
  /**
22
- * Runs an Effect as a Promise while re-throwing the original error directly,
23
- * bypassing Effect.runPromise's FiberFailure wrapper.
165
+ * Runs an Effect as a Promise and throws the most meaningful errors.
24
166
  */
25
167
  declare function runPromise<T>(effect: Effect.Effect<T, unknown>, options?: RunPromiseOptions): Promise<T>;
26
168
 
27
- declare function toStandardSchema<A, I>(schema: Schema.Schema<A, I>): Schema$1<I, A>;
169
+ declare function toStandardSchema<S extends Schema.ConstraintDecoder<any>>(schema: S): Schema$1<S['Encoded'], S['Type']> & S;
28
170
 
29
- export { EffectSchemaToJsonSchemaConverter, extractErrorFromCause, runPromise, toStandardSchema };
30
- export type { RunPromiseOptions };
171
+ export { EffectSchemaToJsonSchemaConverter, InferEffectServices, catchORPCError, catchORPCErrorCode, catchORPCErrorCodes, createEffectClient, middlewareGen, runPromise, toStandardSchema };
172
+ export type { AnyMiddlewareGen, EffectClient, MiddlewareGen, MiddlewareGenNext, MiddlewareGenOptions, RunPromiseOptions };
package/dist/index.mjs CHANGED
@@ -1,27 +1,97 @@
1
- import { JSONSchema } from 'effect';
2
- export { e as extractErrorFromCause, h as handlerGen, r as runPromise } from './shared/experimental-effect.dulfW-wX.mjs';
3
- export { t as toStandardSchema } from './shared/experimental-effect.B68ZlIzV.mjs';
4
- import '@orpc/server';
5
- import '@orpc/shared';
1
+ import { RECURSIVE_CLIENT_UNWRAP_KEYS, resolveClientRest } from '@orpc/client';
2
+ import { isTypescriptObject, anyAbortSignal, getOwn } from '@orpc/shared';
3
+ import { Effect, Schema, Function, Context } from 'effect';
4
+ import { StandardJsonSchemaConverter } from '@orpc/json-schema';
5
+ import { ORPCError } from '@orpc/server';
6
+ import { r as runPromise } from './shared/experimental-effect._sKLzweg.mjs';
7
+ export { h as handlerGen } from './shared/experimental-effect._sKLzweg.mjs';
8
+ export { t as toStandardSchema } from './shared/experimental-effect.YQKompVf.mjs';
6
9
  import '@orpc/contract';
7
10
 
11
+ function callAsEffect(client, ...rest) {
12
+ const [input, options] = resolveClientRest(rest);
13
+ return Effect.tryPromise({
14
+ try: (signal) => client(input, {
15
+ ...options,
16
+ signal: anyAbortSignal([options.signal, signal])
17
+ }),
18
+ catch: (error) => error
19
+ });
20
+ }
21
+ function createEffectClient(client) {
22
+ const cache = /* @__PURE__ */ new Map();
23
+ const proxy = new Proxy((...rest) => callAsEffect(client, ...rest), {
24
+ get(target, prop) {
25
+ if (typeof prop !== "string" || RECURSIVE_CLIENT_UNWRAP_KEYS.has(prop)) {
26
+ return Reflect.get(target, prop);
27
+ }
28
+ let effectClient = cache.get(prop);
29
+ if (effectClient === void 0) {
30
+ const value = client[prop];
31
+ if (!isTypescriptObject(value)) {
32
+ return value;
33
+ }
34
+ effectClient = createEffectClient(value);
35
+ cache.set(prop, effectClient);
36
+ }
37
+ return effectClient;
38
+ }
39
+ });
40
+ return proxy;
41
+ }
42
+
8
43
  class EffectSchemaToJsonSchemaConverter {
44
+ converter = new StandardJsonSchemaConverter();
9
45
  condition(schema, _direction) {
10
46
  return schema?.["~standard"].vendor === "effect";
11
47
  }
12
48
  convert(schema, direction) {
13
49
  const effectSchema = schema;
14
- const jsonSchema = JSONSchema.make(effectSchema, { target: "jsonSchema2020-12" });
15
- let optional = false;
16
- try {
17
- const result = effectSchema["~standard"].validate(void 0);
18
- if (!(result instanceof Promise) && !result.issues) {
19
- optional = direction === "input" ? true : result.value === void 0;
20
- }
21
- } catch {
22
- }
23
- return [jsonSchema, optional];
50
+ const standardJsonSchema = Schema.toStandardJSONSchemaV1(effectSchema);
51
+ return this.converter.convert(standardJsonSchema, direction);
24
52
  }
25
53
  }
26
54
 
27
- export { EffectSchemaToJsonSchemaConverter };
55
+ const catchORPCError = Function.dual(
56
+ 2,
57
+ (self, f) => self.pipe(
58
+ Effect.catchIf((error) => error instanceof ORPCError, f)
59
+ )
60
+ );
61
+ const catchORPCErrorCode = Function.dual(
62
+ 3,
63
+ (self, code, f) => self.pipe(
64
+ Effect.catchIf((error) => error instanceof ORPCError && error.code === code, f)
65
+ )
66
+ );
67
+ const catchORPCErrorCodes = Function.dual(
68
+ 2,
69
+ (self, cases) => self.pipe(
70
+ Effect.catchIf(
71
+ (error) => error instanceof ORPCError && typeof getOwn(cases, error.code) === "function",
72
+ (error) => getOwn(cases, error.code)(error)
73
+ )
74
+ )
75
+ );
76
+
77
+ function middlewareGen(middleware) {
78
+ const mid = (opts, input, done) => {
79
+ const next = (...rest) => Effect.tryPromise({
80
+ try: async () => opts.next(...rest),
81
+ catch: (error) => error
82
+ });
83
+ let ef = Effect.gen(() => middleware({ ...opts, next }, input, done));
84
+ if (Context.isContext(opts.context["effect/context"])) {
85
+ ef = ef.pipe(Effect.provide(opts.context["effect/context"]));
86
+ }
87
+ if (typeof opts.context["effect/wrap"] === "function") {
88
+ const intercept = opts.context["effect/wrap"];
89
+ ef = intercept(ef, opts);
90
+ }
91
+ return runPromise(ef, { signal: opts.signal });
92
+ };
93
+ Object.defineProperty(mid, "name", { value: middleware.name });
94
+ return mid;
95
+ }
96
+
97
+ export { EffectSchemaToJsonSchemaConverter, catchORPCError, catchORPCErrorCode, catchORPCErrorCodes, createEffectClient, middlewareGen, runPromise };