@orpc/experimental-effect 2.0.0-beta.3 → 2.0.0-beta.31
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 +71 -101
- package/dist/extensions/effect.d.mts +7 -8
- package/dist/extensions/effect.d.ts +7 -8
- package/dist/extensions/effect.mjs +1 -1
- package/dist/extensions/input-output.d.mts +18 -18
- package/dist/extensions/input-output.d.ts +18 -18
- package/dist/extensions/input-output.mjs +8 -5
- package/dist/index.d.mts +111 -11
- package/dist/index.d.ts +111 -11
- package/dist/index.mjs +32 -16
- package/dist/shared/{experimental-effect.CuTs7Ks0.d.mts → experimental-effect.BupPDCRJ.d.mts} +21 -9
- package/dist/shared/{experimental-effect.CuTs7Ks0.d.ts → experimental-effect.BupPDCRJ.d.ts} +21 -9
- package/dist/shared/experimental-effect.C9oJcd5q.mjs +34 -0
- package/dist/shared/{experimental-effect.B68ZlIzV.mjs → experimental-effect.YQKompVf.mjs} +1 -1
- package/package.json +17 -8
- package/dist/shared/experimental-effect.dulfW-wX.mjs +0 -40
|
@@ -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.
|
|
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)(
|
|
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)(
|
|
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)(
|
|
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)(
|
|
23
|
+
return OriginalBuilderOutput.bind(this)(isEffectConstraintDecoder(schema) ? toStandardSchema(schema) : schema);
|
|
21
24
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,30 +1,130 @@
|
|
|
1
|
-
export { H as HandlerGen, I as InferYieldError, W as WithEffectContext, h as handlerGen } from './shared/experimental-effect.
|
|
1
|
+
export { H as HandlerGen, I as InferYieldError, W as WithEffectContext, h as handlerGen } from './shared/experimental-effect.BupPDCRJ.mjs';
|
|
2
2
|
import { AnySchema, Schema as Schema$1 } from '@orpc/contract';
|
|
3
3
|
import { JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchema } from '@orpc/json-schema';
|
|
4
|
-
import {
|
|
5
|
-
import '
|
|
6
|
-
import 'effect/Utils';
|
|
4
|
+
import { AnyORPCError, ORPCError, ORPCErrorCode } from '@orpc/server';
|
|
5
|
+
import { Effect, Schema } from 'effect';
|
|
7
6
|
|
|
7
|
+
/**
|
|
8
|
+
* A JSON Schema converter for Effect Schema, built on top of
|
|
9
|
+
* [Effect Schema to JSON Schema](https://effect.website/docs/schema/json-schema/).
|
|
10
|
+
* Useful with tools such as the OpenAPI Generator.
|
|
11
|
+
*
|
|
12
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#json-schema-converter | Effect Integration - JSON Schema Converter}
|
|
13
|
+
*/
|
|
8
14
|
declare class EffectSchemaToJsonSchemaConverter implements JsonSchemaConverter {
|
|
15
|
+
private readonly converter;
|
|
9
16
|
condition(schema: AnySchema | undefined, _direction: JsonSchemaConverterDirection): boolean;
|
|
10
17
|
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
21
|
+
* Recovers from `ORPCError` failures in the error channel of an effect.
|
|
22
|
+
* Other failures re-fail with their original cause.
|
|
23
|
+
*
|
|
24
|
+
* Supports both data-first `catchORPCError(effect, handler)` and data-last
|
|
25
|
+
* `effect.pipe(catchORPCError(handler))` styles.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* declare const program: Effect.Effect<string, ORPCError<'NOT_FOUND', undefined> | TypeError>
|
|
30
|
+
*
|
|
31
|
+
* const recovered = program.pipe(
|
|
32
|
+
* catchORPCError(error => Effect.succeed(`caught ${error.code}`)),
|
|
33
|
+
* )
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
|
|
37
|
+
*/
|
|
38
|
+
declare const catchORPCError: {
|
|
39
|
+
<E, A2, E2, R2>(f: (error: Extract<E, AnyORPCError>) => Effect.Effect<A2, E2, R2>): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A | A2, E2 | Exclude<E, AnyORPCError>, R | R2>;
|
|
40
|
+
<A, E, R, A2, E2, R2>(self: Effect.Effect<A, E, R>, f: (error: Extract<E, AnyORPCError>) => Effect.Effect<A2, E2, R2>): Effect.Effect<A | A2, E2 | Exclude<E, AnyORPCError>, R | R2>;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Recovers from `ORPCError` failures with a matching code in the error channel of an effect.
|
|
44
|
+
* Other failures re-fail with their original cause. The `code` argument is
|
|
45
|
+
* suggested and restricted to the codes present in the error channel.
|
|
46
|
+
*
|
|
47
|
+
* Supports both data-first `catchORPCErrorCode(effect, code, handler)` and
|
|
48
|
+
* data-last `effect.pipe(catchORPCErrorCode(code, handler))` styles.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* declare const program: Effect.Effect<
|
|
53
|
+
* string,
|
|
54
|
+
* ORPCError<'NOT_FOUND', { id: string }> | ORPCError<'CONFLICT', undefined> | TypeError
|
|
55
|
+
* >
|
|
56
|
+
*
|
|
57
|
+
* const recovered = program.pipe(
|
|
58
|
+
* catchORPCErrorCode('NOT_FOUND', error => Effect.succeed(error.data.id)),
|
|
59
|
+
* )
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
|
|
16
63
|
*/
|
|
17
|
-
declare
|
|
64
|
+
declare const catchORPCErrorCode: {
|
|
65
|
+
<const TCode extends E extends ORPCError<infer TCode, any> ? TCode : never, E, A2, E2, R2>(code: TCode, f: (error: Extract<E, ORPCError<TCode, any>>) => 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>;
|
|
66
|
+
<A, E, R, const TCode extends E extends ORPCError<infer TCode, any> ? TCode : never, A2, E2, R2>(self: Effect.Effect<A, E, R>, code: TCode, f: (error: Extract<E, ORPCError<TCode, any>>) => Effect.Effect<A2, E2, R2>): Effect.Effect<A | A2, E2 | Exclude<E, ORPCError<TCode, any>>, R | R2>;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Recovers from `ORPCError` failures using a map of codes to handlers in the
|
|
70
|
+
* error channel of an effect. Other failures re-fail with their original
|
|
71
|
+
* cause. The keys are suggested and restricted to the codes present in the
|
|
72
|
+
* error channel.
|
|
73
|
+
*
|
|
74
|
+
* Supports both data-first `catchORPCErrorCodes(effect, cases)` and data-last
|
|
75
|
+
* `effect.pipe(catchORPCErrorCodes(cases))` styles.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* declare const program: Effect.Effect<
|
|
80
|
+
* string,
|
|
81
|
+
* ORPCError<'NOT_FOUND', { id: string }> | ORPCError<'CONFLICT', undefined> | TypeError
|
|
82
|
+
* >
|
|
83
|
+
*
|
|
84
|
+
* const recovered = program.pipe(
|
|
85
|
+
* catchORPCErrorCodes({
|
|
86
|
+
* NOT_FOUND: error => Effect.succeed(error.data.id),
|
|
87
|
+
* CONFLICT: error => Effect.succeed(error.message),
|
|
88
|
+
* }),
|
|
89
|
+
* )
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
|
|
93
|
+
*/
|
|
94
|
+
declare const catchORPCErrorCodes: {
|
|
95
|
+
<E, Cases extends {
|
|
96
|
+
[K in Extract<E, AnyORPCError>['code']]?: (error: Extract<E, ORPCError<K, any>>) => Effect.Effect<any, any, any>;
|
|
97
|
+
} & (unknown extends E ? object : {
|
|
98
|
+
[K in Exclude<keyof Cases, Extract<E, AnyORPCError>['code']>]: never;
|
|
99
|
+
})>(cases: Cases): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A | {
|
|
100
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<infer A2, any, any> ? A2 : never;
|
|
101
|
+
}[keyof Cases], Exclude<E, ORPCError<keyof Cases & ORPCErrorCode, any>> | {
|
|
102
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, infer E2, any> ? E2 : never;
|
|
103
|
+
}[keyof Cases], R | {
|
|
104
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, any, infer R2> ? R2 : never;
|
|
105
|
+
}[keyof Cases]>;
|
|
106
|
+
<A, E, R, Cases extends {
|
|
107
|
+
[K in Extract<E, AnyORPCError>['code']]?: (error: Extract<E, ORPCError<K, any>>) => Effect.Effect<any, any, any>;
|
|
108
|
+
} & (unknown extends E ? object : {
|
|
109
|
+
[K in Exclude<keyof Cases, Extract<E, AnyORPCError>['code']>]: never;
|
|
110
|
+
})>(self: Effect.Effect<A, E, R>, cases: Cases): Effect.Effect<A | {
|
|
111
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<infer A2, any, any> ? A2 : never;
|
|
112
|
+
}[keyof Cases], Exclude<E, ORPCError<keyof Cases & ORPCErrorCode, any>> | {
|
|
113
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, infer E2, any> ? E2 : never;
|
|
114
|
+
}[keyof Cases], R | {
|
|
115
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, any, infer R2> ? R2 : never;
|
|
116
|
+
}[keyof Cases]>;
|
|
117
|
+
};
|
|
118
|
+
|
|
18
119
|
interface RunPromiseOptions {
|
|
19
120
|
signal?: undefined | AbortSignal;
|
|
20
121
|
}
|
|
21
122
|
/**
|
|
22
|
-
* Runs an Effect as a Promise
|
|
23
|
-
* bypassing Effect.runPromise's FiberFailure wrapper.
|
|
123
|
+
* Runs an Effect as a Promise and throws the most meaningful errors.
|
|
24
124
|
*/
|
|
25
125
|
declare function runPromise<T>(effect: Effect.Effect<T, unknown>, options?: RunPromiseOptions): Promise<T>;
|
|
26
126
|
|
|
27
|
-
declare function toStandardSchema<
|
|
127
|
+
declare function toStandardSchema<S extends Schema.ConstraintDecoder<any>>(schema: S): Schema$1<S['Encoded'], S['Type']> & S;
|
|
28
128
|
|
|
29
|
-
export { EffectSchemaToJsonSchemaConverter,
|
|
129
|
+
export { EffectSchemaToJsonSchemaConverter, catchORPCError, catchORPCErrorCode, catchORPCErrorCodes, runPromise, toStandardSchema };
|
|
30
130
|
export type { RunPromiseOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,30 +1,130 @@
|
|
|
1
|
-
export { H as HandlerGen, I as InferYieldError, W as WithEffectContext, h as handlerGen } from './shared/experimental-effect.
|
|
1
|
+
export { H as HandlerGen, I as InferYieldError, W as WithEffectContext, h as handlerGen } from './shared/experimental-effect.BupPDCRJ.js';
|
|
2
2
|
import { AnySchema, Schema as Schema$1 } from '@orpc/contract';
|
|
3
3
|
import { JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchema } from '@orpc/json-schema';
|
|
4
|
-
import {
|
|
5
|
-
import '
|
|
6
|
-
import 'effect/Utils';
|
|
4
|
+
import { AnyORPCError, ORPCError, ORPCErrorCode } from '@orpc/server';
|
|
5
|
+
import { Effect, Schema } from 'effect';
|
|
7
6
|
|
|
7
|
+
/**
|
|
8
|
+
* A JSON Schema converter for Effect Schema, built on top of
|
|
9
|
+
* [Effect Schema to JSON Schema](https://effect.website/docs/schema/json-schema/).
|
|
10
|
+
* Useful with tools such as the OpenAPI Generator.
|
|
11
|
+
*
|
|
12
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#json-schema-converter | Effect Integration - JSON Schema Converter}
|
|
13
|
+
*/
|
|
8
14
|
declare class EffectSchemaToJsonSchemaConverter implements JsonSchemaConverter {
|
|
15
|
+
private readonly converter;
|
|
9
16
|
condition(schema: AnySchema | undefined, _direction: JsonSchemaConverterDirection): boolean;
|
|
10
17
|
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
21
|
+
* Recovers from `ORPCError` failures in the error channel of an effect.
|
|
22
|
+
* Other failures re-fail with their original cause.
|
|
23
|
+
*
|
|
24
|
+
* Supports both data-first `catchORPCError(effect, handler)` and data-last
|
|
25
|
+
* `effect.pipe(catchORPCError(handler))` styles.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* declare const program: Effect.Effect<string, ORPCError<'NOT_FOUND', undefined> | TypeError>
|
|
30
|
+
*
|
|
31
|
+
* const recovered = program.pipe(
|
|
32
|
+
* catchORPCError(error => Effect.succeed(`caught ${error.code}`)),
|
|
33
|
+
* )
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
|
|
37
|
+
*/
|
|
38
|
+
declare const catchORPCError: {
|
|
39
|
+
<E, A2, E2, R2>(f: (error: Extract<E, AnyORPCError>) => Effect.Effect<A2, E2, R2>): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A | A2, E2 | Exclude<E, AnyORPCError>, R | R2>;
|
|
40
|
+
<A, E, R, A2, E2, R2>(self: Effect.Effect<A, E, R>, f: (error: Extract<E, AnyORPCError>) => Effect.Effect<A2, E2, R2>): Effect.Effect<A | A2, E2 | Exclude<E, AnyORPCError>, R | R2>;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Recovers from `ORPCError` failures with a matching code in the error channel of an effect.
|
|
44
|
+
* Other failures re-fail with their original cause. The `code` argument is
|
|
45
|
+
* suggested and restricted to the codes present in the error channel.
|
|
46
|
+
*
|
|
47
|
+
* Supports both data-first `catchORPCErrorCode(effect, code, handler)` and
|
|
48
|
+
* data-last `effect.pipe(catchORPCErrorCode(code, handler))` styles.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* declare const program: Effect.Effect<
|
|
53
|
+
* string,
|
|
54
|
+
* ORPCError<'NOT_FOUND', { id: string }> | ORPCError<'CONFLICT', undefined> | TypeError
|
|
55
|
+
* >
|
|
56
|
+
*
|
|
57
|
+
* const recovered = program.pipe(
|
|
58
|
+
* catchORPCErrorCode('NOT_FOUND', error => Effect.succeed(error.data.id)),
|
|
59
|
+
* )
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
|
|
16
63
|
*/
|
|
17
|
-
declare
|
|
64
|
+
declare const catchORPCErrorCode: {
|
|
65
|
+
<const TCode extends E extends ORPCError<infer TCode, any> ? TCode : never, E, A2, E2, R2>(code: TCode, f: (error: Extract<E, ORPCError<TCode, any>>) => 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>;
|
|
66
|
+
<A, E, R, const TCode extends E extends ORPCError<infer TCode, any> ? TCode : never, A2, E2, R2>(self: Effect.Effect<A, E, R>, code: TCode, f: (error: Extract<E, ORPCError<TCode, any>>) => Effect.Effect<A2, E2, R2>): Effect.Effect<A | A2, E2 | Exclude<E, ORPCError<TCode, any>>, R | R2>;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Recovers from `ORPCError` failures using a map of codes to handlers in the
|
|
70
|
+
* error channel of an effect. Other failures re-fail with their original
|
|
71
|
+
* cause. The keys are suggested and restricted to the codes present in the
|
|
72
|
+
* error channel.
|
|
73
|
+
*
|
|
74
|
+
* Supports both data-first `catchORPCErrorCodes(effect, cases)` and data-last
|
|
75
|
+
* `effect.pipe(catchORPCErrorCodes(cases))` styles.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* declare const program: Effect.Effect<
|
|
80
|
+
* string,
|
|
81
|
+
* ORPCError<'NOT_FOUND', { id: string }> | ORPCError<'CONFLICT', undefined> | TypeError
|
|
82
|
+
* >
|
|
83
|
+
*
|
|
84
|
+
* const recovered = program.pipe(
|
|
85
|
+
* catchORPCErrorCodes({
|
|
86
|
+
* NOT_FOUND: error => Effect.succeed(error.data.id),
|
|
87
|
+
* CONFLICT: error => Effect.succeed(error.message),
|
|
88
|
+
* }),
|
|
89
|
+
* )
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#catching-orpcerrors | Effect Integration - Catching ORPCErrors}
|
|
93
|
+
*/
|
|
94
|
+
declare const catchORPCErrorCodes: {
|
|
95
|
+
<E, Cases extends {
|
|
96
|
+
[K in Extract<E, AnyORPCError>['code']]?: (error: Extract<E, ORPCError<K, any>>) => Effect.Effect<any, any, any>;
|
|
97
|
+
} & (unknown extends E ? object : {
|
|
98
|
+
[K in Exclude<keyof Cases, Extract<E, AnyORPCError>['code']>]: never;
|
|
99
|
+
})>(cases: Cases): <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A | {
|
|
100
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<infer A2, any, any> ? A2 : never;
|
|
101
|
+
}[keyof Cases], Exclude<E, ORPCError<keyof Cases & ORPCErrorCode, any>> | {
|
|
102
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, infer E2, any> ? E2 : never;
|
|
103
|
+
}[keyof Cases], R | {
|
|
104
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, any, infer R2> ? R2 : never;
|
|
105
|
+
}[keyof Cases]>;
|
|
106
|
+
<A, E, R, Cases extends {
|
|
107
|
+
[K in Extract<E, AnyORPCError>['code']]?: (error: Extract<E, ORPCError<K, any>>) => Effect.Effect<any, any, any>;
|
|
108
|
+
} & (unknown extends E ? object : {
|
|
109
|
+
[K in Exclude<keyof Cases, Extract<E, AnyORPCError>['code']>]: never;
|
|
110
|
+
})>(self: Effect.Effect<A, E, R>, cases: Cases): Effect.Effect<A | {
|
|
111
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<infer A2, any, any> ? A2 : never;
|
|
112
|
+
}[keyof Cases], Exclude<E, ORPCError<keyof Cases & ORPCErrorCode, any>> | {
|
|
113
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, infer E2, any> ? E2 : never;
|
|
114
|
+
}[keyof Cases], R | {
|
|
115
|
+
[K in keyof Cases]: Cases[K] extends (...args: any[]) => Effect.Effect<any, any, infer R2> ? R2 : never;
|
|
116
|
+
}[keyof Cases]>;
|
|
117
|
+
};
|
|
118
|
+
|
|
18
119
|
interface RunPromiseOptions {
|
|
19
120
|
signal?: undefined | AbortSignal;
|
|
20
121
|
}
|
|
21
122
|
/**
|
|
22
|
-
* Runs an Effect as a Promise
|
|
23
|
-
* bypassing Effect.runPromise's FiberFailure wrapper.
|
|
123
|
+
* Runs an Effect as a Promise and throws the most meaningful errors.
|
|
24
124
|
*/
|
|
25
125
|
declare function runPromise<T>(effect: Effect.Effect<T, unknown>, options?: RunPromiseOptions): Promise<T>;
|
|
26
126
|
|
|
27
|
-
declare function toStandardSchema<
|
|
127
|
+
declare function toStandardSchema<S extends Schema.ConstraintDecoder<any>>(schema: S): Schema$1<S['Encoded'], S['Type']> & S;
|
|
28
128
|
|
|
29
|
-
export { EffectSchemaToJsonSchemaConverter,
|
|
129
|
+
export { EffectSchemaToJsonSchemaConverter, catchORPCError, catchORPCErrorCode, catchORPCErrorCodes, runPromise, toStandardSchema };
|
|
30
130
|
export type { RunPromiseOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,27 +1,43 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import '@orpc/
|
|
5
|
-
|
|
1
|
+
import { StandardJsonSchemaConverter } from '@orpc/json-schema';
|
|
2
|
+
import { Schema, Function, Effect } from 'effect';
|
|
3
|
+
import { ORPCError } from '@orpc/server';
|
|
4
|
+
import { getOwn } from '@orpc/shared';
|
|
5
|
+
export { h as handlerGen, r as runPromise } from './shared/experimental-effect.C9oJcd5q.mjs';
|
|
6
|
+
export { t as toStandardSchema } from './shared/experimental-effect.YQKompVf.mjs';
|
|
6
7
|
import '@orpc/contract';
|
|
7
8
|
|
|
8
9
|
class EffectSchemaToJsonSchemaConverter {
|
|
10
|
+
converter = new StandardJsonSchemaConverter();
|
|
9
11
|
condition(schema, _direction) {
|
|
10
12
|
return schema?.["~standard"].vendor === "effect";
|
|
11
13
|
}
|
|
12
14
|
convert(schema, direction) {
|
|
13
15
|
const effectSchema = schema;
|
|
14
|
-
const
|
|
15
|
-
|
|
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];
|
|
16
|
+
const standardJsonSchema = Schema.toStandardJSONSchemaV1(effectSchema);
|
|
17
|
+
return this.converter.convert(standardJsonSchema, direction);
|
|
24
18
|
}
|
|
25
19
|
}
|
|
26
20
|
|
|
27
|
-
|
|
21
|
+
const catchORPCError = Function.dual(
|
|
22
|
+
2,
|
|
23
|
+
(self, f) => self.pipe(
|
|
24
|
+
Effect.catchIf((error) => error instanceof ORPCError, f)
|
|
25
|
+
)
|
|
26
|
+
);
|
|
27
|
+
const catchORPCErrorCode = Function.dual(
|
|
28
|
+
3,
|
|
29
|
+
(self, code, f) => self.pipe(
|
|
30
|
+
Effect.catchIf((error) => error instanceof ORPCError && error.code === code, f)
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
const catchORPCErrorCodes = Function.dual(
|
|
34
|
+
2,
|
|
35
|
+
(self, cases) => self.pipe(
|
|
36
|
+
Effect.catchIf(
|
|
37
|
+
(error) => error instanceof ORPCError && typeof getOwn(cases, error.code) === "function",
|
|
38
|
+
(error) => getOwn(cases, error.code)(error)
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
export { EffectSchemaToJsonSchemaConverter, catchORPCError, catchORPCErrorCode, catchORPCErrorCodes };
|
package/dist/shared/{experimental-effect.CuTs7Ks0.d.mts → experimental-effect.BupPDCRJ.d.mts}
RENAMED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { AnyProcedure, Context as Context$1, ORPCErrorConstructorMap, ProcedureHandlerOptions, ProcedureHandler, AnyORPCError } from '@orpc/server';
|
|
2
|
-
import { YieldWrap } from 'effect/Utils';
|
|
3
2
|
import { Context, Effect } from 'effect';
|
|
4
3
|
|
|
4
|
+
/**
|
|
5
|
+
* A context shape that provides Effect services to effectful handlers
|
|
6
|
+
* through the oRPC context in a typesafe way.
|
|
7
|
+
*
|
|
8
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#effect-services | Effect Integration - Effect Services}
|
|
9
|
+
*/
|
|
5
10
|
interface WithEffectContext<Services> {
|
|
6
11
|
/**
|
|
7
12
|
* A pre-built Effect context providing the services available within this oRPC context.
|
|
@@ -14,13 +19,13 @@ interface WithEffectContext<Services> {
|
|
|
14
19
|
* interface ServerContext extends WithEffectContext<Random | Logger> {}
|
|
15
20
|
*
|
|
16
21
|
* const context: ServerContext = {
|
|
17
|
-
* '
|
|
22
|
+
* 'effect/context': Context.empty().pipe(
|
|
18
23
|
* Context.add(Random, { next: Effect.sync(() => Math.random()) }),
|
|
19
24
|
* ),
|
|
20
25
|
* }
|
|
21
26
|
* ```
|
|
22
27
|
*/
|
|
23
|
-
['
|
|
28
|
+
['effect/context']: Context.Context<Services>;
|
|
24
29
|
/**
|
|
25
30
|
* An optional hook to wrap any effect before it is executed within this oRPC context.
|
|
26
31
|
* Useful for adding observability, tracing, or error handling.
|
|
@@ -37,23 +42,30 @@ interface WithEffectContext<Services> {
|
|
|
37
42
|
* )
|
|
38
43
|
*
|
|
39
44
|
* const context: ServerContext = {
|
|
40
|
-
* '
|
|
41
|
-
* '
|
|
45
|
+
* 'effect/context': Context.empty(),
|
|
46
|
+
* 'effect/wrap': (effect) => effect.pipe(Effect.provide(TracingLive)),
|
|
42
47
|
* }
|
|
43
48
|
* ```
|
|
44
49
|
*/
|
|
45
|
-
['
|
|
50
|
+
['effect/wrap']?: <A, E>(effect: Effect.Effect<A, E>, opts: {
|
|
46
51
|
path: string[];
|
|
47
52
|
procedure: AnyProcedure;
|
|
48
53
|
signal?: undefined | AbortSignal;
|
|
49
54
|
}) => Effect.Effect<A, E>;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
type InferYieldError<Eff> = [Eff] extends [never] ? never : [Eff] extends [
|
|
53
|
-
interface HandlerGen<TCurrentContext extends Context$1, TInput, TYield extends
|
|
57
|
+
type InferYieldError<Eff> = [Eff] extends [never] ? never : [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E : never;
|
|
58
|
+
interface HandlerGen<TCurrentContext extends Context$1, TInput, TYield extends Effect.Effect<any, any, TCurrentContext extends WithEffectContext<infer S> ? S : never>, TReturn, TErrorConstructorMap extends ORPCErrorConstructorMap<any>> {
|
|
54
59
|
(opts: ProcedureHandlerOptions<TCurrentContext, TInput, TErrorConstructorMap>, input: TInput): Generator<TYield, TReturn, never>;
|
|
55
60
|
}
|
|
56
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Creates a procedure handler from an Effect generator function.
|
|
63
|
+
* Inside the generator you can yield Effect operations, and `handlerGen`
|
|
64
|
+
* handles the execution and error handling for you.
|
|
65
|
+
*
|
|
66
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#effectful-handlers | Effect Integration - Effectful Handlers}
|
|
67
|
+
*/
|
|
68
|
+
declare function handlerGen<TCurrentContext extends Context$1, TInput, TErrorConstructorMap extends ORPCErrorConstructorMap<any>, TYield extends Effect.Effect<any, any, TCurrentContext extends WithEffectContext<infer S> ? S : never>, TReturn>(handler: HandlerGen<TCurrentContext, TInput, TYield, TReturn, TErrorConstructorMap>): ProcedureHandler<TCurrentContext, TInput, TReturn | Extract<InferYieldError<TYield>, AnyORPCError>, TErrorConstructorMap>;
|
|
57
69
|
|
|
58
70
|
export { handlerGen as h };
|
|
59
71
|
export type { HandlerGen as H, InferYieldError as I, WithEffectContext as W };
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { AnyProcedure, Context as Context$1, ORPCErrorConstructorMap, ProcedureHandlerOptions, ProcedureHandler, AnyORPCError } from '@orpc/server';
|
|
2
|
-
import { YieldWrap } from 'effect/Utils';
|
|
3
2
|
import { Context, Effect } from 'effect';
|
|
4
3
|
|
|
4
|
+
/**
|
|
5
|
+
* A context shape that provides Effect services to effectful handlers
|
|
6
|
+
* through the oRPC context in a typesafe way.
|
|
7
|
+
*
|
|
8
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#effect-services | Effect Integration - Effect Services}
|
|
9
|
+
*/
|
|
5
10
|
interface WithEffectContext<Services> {
|
|
6
11
|
/**
|
|
7
12
|
* A pre-built Effect context providing the services available within this oRPC context.
|
|
@@ -14,13 +19,13 @@ interface WithEffectContext<Services> {
|
|
|
14
19
|
* interface ServerContext extends WithEffectContext<Random | Logger> {}
|
|
15
20
|
*
|
|
16
21
|
* const context: ServerContext = {
|
|
17
|
-
* '
|
|
22
|
+
* 'effect/context': Context.empty().pipe(
|
|
18
23
|
* Context.add(Random, { next: Effect.sync(() => Math.random()) }),
|
|
19
24
|
* ),
|
|
20
25
|
* }
|
|
21
26
|
* ```
|
|
22
27
|
*/
|
|
23
|
-
['
|
|
28
|
+
['effect/context']: Context.Context<Services>;
|
|
24
29
|
/**
|
|
25
30
|
* An optional hook to wrap any effect before it is executed within this oRPC context.
|
|
26
31
|
* Useful for adding observability, tracing, or error handling.
|
|
@@ -37,23 +42,30 @@ interface WithEffectContext<Services> {
|
|
|
37
42
|
* )
|
|
38
43
|
*
|
|
39
44
|
* const context: ServerContext = {
|
|
40
|
-
* '
|
|
41
|
-
* '
|
|
45
|
+
* 'effect/context': Context.empty(),
|
|
46
|
+
* 'effect/wrap': (effect) => effect.pipe(Effect.provide(TracingLive)),
|
|
42
47
|
* }
|
|
43
48
|
* ```
|
|
44
49
|
*/
|
|
45
|
-
['
|
|
50
|
+
['effect/wrap']?: <A, E>(effect: Effect.Effect<A, E>, opts: {
|
|
46
51
|
path: string[];
|
|
47
52
|
procedure: AnyProcedure;
|
|
48
53
|
signal?: undefined | AbortSignal;
|
|
49
54
|
}) => Effect.Effect<A, E>;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
type InferYieldError<Eff> = [Eff] extends [never] ? never : [Eff] extends [
|
|
53
|
-
interface HandlerGen<TCurrentContext extends Context$1, TInput, TYield extends
|
|
57
|
+
type InferYieldError<Eff> = [Eff] extends [never] ? never : [Eff] extends [Effect.Effect<infer _A, infer E, infer _R>] ? E : never;
|
|
58
|
+
interface HandlerGen<TCurrentContext extends Context$1, TInput, TYield extends Effect.Effect<any, any, TCurrentContext extends WithEffectContext<infer S> ? S : never>, TReturn, TErrorConstructorMap extends ORPCErrorConstructorMap<any>> {
|
|
54
59
|
(opts: ProcedureHandlerOptions<TCurrentContext, TInput, TErrorConstructorMap>, input: TInput): Generator<TYield, TReturn, never>;
|
|
55
60
|
}
|
|
56
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Creates a procedure handler from an Effect generator function.
|
|
63
|
+
* Inside the generator you can yield Effect operations, and `handlerGen`
|
|
64
|
+
* handles the execution and error handling for you.
|
|
65
|
+
*
|
|
66
|
+
* @see {@link https://orpc.dev/docs/integrations/effect#effectful-handlers | Effect Integration - Effectful Handlers}
|
|
67
|
+
*/
|
|
68
|
+
declare function handlerGen<TCurrentContext extends Context$1, TInput, TErrorConstructorMap extends ORPCErrorConstructorMap<any>, TYield extends Effect.Effect<any, any, TCurrentContext extends WithEffectContext<infer S> ? S : never>, TReturn>(handler: HandlerGen<TCurrentContext, TInput, TYield, TReturn, TErrorConstructorMap>): ProcedureHandler<TCurrentContext, TInput, TReturn | Extract<InferYieldError<TYield>, AnyORPCError>, TErrorConstructorMap>;
|
|
57
69
|
|
|
58
70
|
export { handlerGen as h };
|
|
59
71
|
export type { HandlerGen as H, InferYieldError as I, WithEffectContext as W };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ORPCError } from '@orpc/server';
|
|
2
|
+
import { Effect, Exit, Cause, Context } from 'effect';
|
|
3
|
+
import { AbortError } from '@orpc/shared';
|
|
4
|
+
|
|
5
|
+
async function runPromise(effect, options = {}) {
|
|
6
|
+
const exit = await Effect.runPromiseExit(effect, options);
|
|
7
|
+
if (Exit.isSuccess(exit)) {
|
|
8
|
+
return exit.value;
|
|
9
|
+
}
|
|
10
|
+
if (Cause.hasInterruptsOnly(exit.cause)) {
|
|
11
|
+
if (options.signal?.aborted) {
|
|
12
|
+
throw options.signal.reason;
|
|
13
|
+
}
|
|
14
|
+
throw new AbortError("All fibers interrupted without error");
|
|
15
|
+
}
|
|
16
|
+
throw Cause.squash(exit.cause);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const succeedOnORPCError = Effect.catch((error) => error instanceof ORPCError ? Effect.succeed(error) : Effect.fail(error));
|
|
20
|
+
function handlerGen(handler) {
|
|
21
|
+
return (opts, input) => {
|
|
22
|
+
let ef = Effect.gen(() => handler(opts, input)).pipe(succeedOnORPCError);
|
|
23
|
+
if (Context.isContext(opts.context["effect/context"])) {
|
|
24
|
+
ef = ef.pipe(Effect.provide(opts.context["effect/context"]));
|
|
25
|
+
}
|
|
26
|
+
if (typeof opts.context["effect/wrap"] === "function") {
|
|
27
|
+
const intercept = opts.context["effect/wrap"];
|
|
28
|
+
ef = intercept(ef, opts);
|
|
29
|
+
}
|
|
30
|
+
return runPromise(ef, { signal: opts.signal });
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { handlerGen as h, runPromise as r };
|
|
@@ -2,7 +2,7 @@ import { getHiddenMetaPlugins, setHiddenMetaPlugins } from '@orpc/contract';
|
|
|
2
2
|
import { Schema } from 'effect';
|
|
3
3
|
|
|
4
4
|
function toStandardSchema(schema) {
|
|
5
|
-
const converted = Schema.
|
|
5
|
+
const converted = Schema.toStandardSchemaV1(schema);
|
|
6
6
|
const metaPlugins = getHiddenMetaPlugins(schema);
|
|
7
7
|
if (metaPlugins) {
|
|
8
8
|
setHiddenMetaPlugins(converted, metaPlugins);
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orpc/experimental-effect",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.31",
|
|
5
|
+
"description": "Effect integration for oRPC: build typesafe procedures with Effect",
|
|
5
6
|
"license": "MIT",
|
|
7
|
+
"funding": [
|
|
8
|
+
"https://github.com/sponsors/dinwwwh",
|
|
9
|
+
"https://opencollective.com/middleapi"
|
|
10
|
+
],
|
|
6
11
|
"homepage": "https://orpc.dev",
|
|
7
12
|
"repository": {
|
|
8
13
|
"type": "git",
|
|
@@ -11,7 +16,11 @@
|
|
|
11
16
|
},
|
|
12
17
|
"keywords": [
|
|
13
18
|
"orpc",
|
|
14
|
-
"effect"
|
|
19
|
+
"effect",
|
|
20
|
+
"effect-ts",
|
|
21
|
+
"rpc",
|
|
22
|
+
"typescript",
|
|
23
|
+
"typesafe"
|
|
15
24
|
],
|
|
16
25
|
"sideEffects": [
|
|
17
26
|
"./dist/extensions/effect.mjs",
|
|
@@ -39,16 +48,16 @@
|
|
|
39
48
|
"dist"
|
|
40
49
|
],
|
|
41
50
|
"peerDependencies": {
|
|
42
|
-
"effect": ">=
|
|
51
|
+
"effect": ">=4.0.0-beta.90"
|
|
43
52
|
},
|
|
44
53
|
"dependencies": {
|
|
45
|
-
"@orpc/
|
|
46
|
-
"@orpc/
|
|
47
|
-
"@orpc/
|
|
48
|
-
"@orpc/
|
|
54
|
+
"@orpc/contract": "2.0.0-beta.31",
|
|
55
|
+
"@orpc/server": "2.0.0-beta.31",
|
|
56
|
+
"@orpc/json-schema": "2.0.0-beta.31",
|
|
57
|
+
"@orpc/shared": "2.0.0-beta.31"
|
|
49
58
|
},
|
|
50
59
|
"devDependencies": {
|
|
51
|
-
"effect": "
|
|
60
|
+
"effect": "4.0.0-rc.109"
|
|
52
61
|
},
|
|
53
62
|
"scripts": {
|
|
54
63
|
"build": "unbuild",
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { ORPCError } from '@orpc/server';
|
|
2
|
-
import { Effect, Exit, Cause, FiberId, Context } from 'effect';
|
|
3
|
-
import { AbortError } from '@orpc/shared';
|
|
4
|
-
|
|
5
|
-
function extractErrorFromCause(cause) {
|
|
6
|
-
return Cause.match(cause, {
|
|
7
|
-
onFail: (error) => error,
|
|
8
|
-
onDie: (defect) => defect,
|
|
9
|
-
onInterrupt: (fiberId) => new AbortError(`Fiber interrupted: ${FiberId.threadName(fiberId)}`),
|
|
10
|
-
onEmpty: new Error("Effect failed with no error information"),
|
|
11
|
-
// Mirrors native try/finally: if the finalizer (right) also throws,
|
|
12
|
-
// it overwrites the original (left) — same behaviour as JS would produce
|
|
13
|
-
onSequential: (_left, right) => right,
|
|
14
|
-
onParallel: (left, _right) => left
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
async function runPromise(effect, options = {}) {
|
|
18
|
-
const exit = await Effect.runPromiseExit(effect, options);
|
|
19
|
-
if (Exit.isSuccess(exit)) {
|
|
20
|
-
return exit.value;
|
|
21
|
-
}
|
|
22
|
-
throw extractErrorFromCause(exit.cause);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const succeedOnORPCError = Effect.catchAll((error) => error instanceof ORPCError ? Effect.succeed(error) : Effect.fail(error));
|
|
26
|
-
function handlerGen(handler) {
|
|
27
|
-
return (opts, input) => {
|
|
28
|
-
let ef = Effect.gen(() => handler(opts, input)).pipe(succeedOnORPCError);
|
|
29
|
-
if (Context.isContext(opts.context["~effect/context"])) {
|
|
30
|
-
ef = ef.pipe(Effect.provide(opts.context["~effect/context"]));
|
|
31
|
-
}
|
|
32
|
-
if (typeof opts.context["~effect/wrap"] === "function") {
|
|
33
|
-
const intercept = opts.context["~effect/wrap"];
|
|
34
|
-
ef = intercept(ef, opts);
|
|
35
|
-
}
|
|
36
|
-
return runPromise(ef, { signal: opts.signal });
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export { extractErrorFromCause as e, handlerGen as h, runPromise as r };
|