effect-orpc 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/dist/index.js +352 -72
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/contract.ts +491 -0
- package/src/effect-builder.ts +40 -88
- package/src/effect-runtime.ts +134 -0
- package/src/eoc.ts +499 -0
- package/src/index.ts +18 -3
- package/src/tagged-error.ts +24 -4
- package/src/tests/contract.test.ts +346 -0
- package/src/tests/effect-error-map.test.ts +22 -3
- package/src/tests/parity-shared.ts +32 -0
- package/src/tests/parity.contract-builder-variants.test.ts +192 -0
- package/src/tests/parity.contract-builder.test.ts +222 -0
- package/src/tests/parity.effect-builder.test.ts +193 -0
- package/src/tests/parity.effect-procedure.test.ts +124 -0
- package/src/tests/parity.implementer-variants.test.ts +249 -0
- package/src/tests/parity.implementer.test.ts +280 -0
- package/src/tests/shared.ts +2 -0
- package/src/types/index.ts +0 -16
- package/src/types/variants.ts +25 -16
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { ORPCError } from "@orpc/contract";
|
|
2
|
+
import type {
|
|
3
|
+
Context,
|
|
4
|
+
ProcedureHandler,
|
|
5
|
+
ProcedureHandlerOptions,
|
|
6
|
+
} from "@orpc/server";
|
|
7
|
+
import type { ManagedRuntime } from "effect";
|
|
8
|
+
import { Cause, Effect, Exit, FiberRefs } from "effect";
|
|
9
|
+
|
|
10
|
+
import { getCurrentFiberRefs } from "./fiber-context-bridge";
|
|
11
|
+
import type { EffectErrorConstructorMap, EffectErrorMap } from "./tagged-error";
|
|
12
|
+
import {
|
|
13
|
+
createEffectErrorConstructorMap,
|
|
14
|
+
isORPCTaggedError,
|
|
15
|
+
} from "./tagged-error";
|
|
16
|
+
import type { EffectProcedureHandler, EffectSpanConfig } from "./types";
|
|
17
|
+
|
|
18
|
+
export function toORPCErrorFromCause(
|
|
19
|
+
cause: Cause.Cause<unknown>,
|
|
20
|
+
): ORPCError<string, unknown> {
|
|
21
|
+
return Cause.match(cause, {
|
|
22
|
+
onDie(defect) {
|
|
23
|
+
return new ORPCError("INTERNAL_SERVER_ERROR", {
|
|
24
|
+
cause: defect,
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
onFail(error) {
|
|
28
|
+
if (isORPCTaggedError(error)) {
|
|
29
|
+
return error.toORPCError();
|
|
30
|
+
}
|
|
31
|
+
if (error instanceof ORPCError) {
|
|
32
|
+
return error;
|
|
33
|
+
}
|
|
34
|
+
return new ORPCError("INTERNAL_SERVER_ERROR", {
|
|
35
|
+
cause: error,
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
onInterrupt(fiberId) {
|
|
39
|
+
return new ORPCError("INTERNAL_SERVER_ERROR", {
|
|
40
|
+
cause: new Error(`${fiberId} Interrupted`),
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
onSequential(left) {
|
|
44
|
+
return left;
|
|
45
|
+
},
|
|
46
|
+
onEmpty: new ORPCError("INTERNAL_SERVER_ERROR", {
|
|
47
|
+
cause: new Error("Unknown error"),
|
|
48
|
+
}),
|
|
49
|
+
onParallel(left) {
|
|
50
|
+
return left;
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createEffectProcedureHandler<
|
|
56
|
+
TCurrentContext extends Context,
|
|
57
|
+
TInput,
|
|
58
|
+
TOutput,
|
|
59
|
+
TEffectErrorMap extends EffectErrorMap,
|
|
60
|
+
TRequirementsProvided,
|
|
61
|
+
TRuntimeError,
|
|
62
|
+
TMeta,
|
|
63
|
+
>(options: {
|
|
64
|
+
runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>;
|
|
65
|
+
effectErrorMap: TEffectErrorMap;
|
|
66
|
+
effectFn: EffectProcedureHandler<
|
|
67
|
+
TCurrentContext,
|
|
68
|
+
TInput,
|
|
69
|
+
TOutput,
|
|
70
|
+
TEffectErrorMap,
|
|
71
|
+
TRequirementsProvided,
|
|
72
|
+
any
|
|
73
|
+
>;
|
|
74
|
+
spanConfig?: EffectSpanConfig;
|
|
75
|
+
defaultCaptureStackTrace: () => string | undefined;
|
|
76
|
+
}): ProcedureHandler<
|
|
77
|
+
TCurrentContext,
|
|
78
|
+
TInput,
|
|
79
|
+
TOutput,
|
|
80
|
+
any,
|
|
81
|
+
TMeta & Record<never, never>
|
|
82
|
+
> {
|
|
83
|
+
const {
|
|
84
|
+
runtime,
|
|
85
|
+
effectErrorMap,
|
|
86
|
+
effectFn,
|
|
87
|
+
spanConfig,
|
|
88
|
+
defaultCaptureStackTrace,
|
|
89
|
+
} = options;
|
|
90
|
+
|
|
91
|
+
return async (opts) => {
|
|
92
|
+
const effectOpts: ProcedureHandlerOptions<
|
|
93
|
+
TCurrentContext,
|
|
94
|
+
TInput,
|
|
95
|
+
EffectErrorConstructorMap<TEffectErrorMap>,
|
|
96
|
+
TMeta & Record<never, never>
|
|
97
|
+
> = {
|
|
98
|
+
context: opts.context,
|
|
99
|
+
input: opts.input,
|
|
100
|
+
path: opts.path,
|
|
101
|
+
procedure: opts.procedure,
|
|
102
|
+
signal: opts.signal,
|
|
103
|
+
lastEventId: opts.lastEventId,
|
|
104
|
+
errors: createEffectErrorConstructorMap(effectErrorMap),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const spanName = spanConfig?.name ?? opts.path.join(".");
|
|
108
|
+
const captureStackTrace =
|
|
109
|
+
spanConfig?.captureStackTrace ?? defaultCaptureStackTrace;
|
|
110
|
+
const resolver = Effect.fnUntraced(effectFn as any);
|
|
111
|
+
const tracedEffect = Effect.withSpan(resolver(effectOpts), spanName, {
|
|
112
|
+
captureStackTrace,
|
|
113
|
+
});
|
|
114
|
+
const parentFiberRefs = getCurrentFiberRefs();
|
|
115
|
+
const effectWithRefs = parentFiberRefs
|
|
116
|
+
? Effect.fiberIdWith((fiberId) =>
|
|
117
|
+
Effect.flatMap(Effect.getFiberRefs, (fiberRefs) =>
|
|
118
|
+
Effect.setFiberRefs(
|
|
119
|
+
FiberRefs.joinAs(fiberRefs, fiberId, parentFiberRefs),
|
|
120
|
+
).pipe(Effect.andThen(tracedEffect)),
|
|
121
|
+
),
|
|
122
|
+
)
|
|
123
|
+
: tracedEffect;
|
|
124
|
+
const exit = await runtime.runPromiseExit(effectWithRefs, {
|
|
125
|
+
signal: opts.signal,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (Exit.isFailure(exit)) {
|
|
129
|
+
throw toORPCErrorFromCause(exit.cause);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return exit.value as TOutput;
|
|
133
|
+
};
|
|
134
|
+
}
|
package/src/eoc.ts
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnySchema,
|
|
3
|
+
ContractProcedure,
|
|
4
|
+
ContractRouter,
|
|
5
|
+
ErrorMap,
|
|
6
|
+
HTTPPath,
|
|
7
|
+
Meta,
|
|
8
|
+
Route,
|
|
9
|
+
Schema,
|
|
10
|
+
} from "@orpc/contract";
|
|
11
|
+
import { isContractProcedure, oc } from "@orpc/contract";
|
|
12
|
+
|
|
13
|
+
import type { EffectErrorMap, MergedEffectErrorMap } from "./tagged-error";
|
|
14
|
+
import { effectErrorMapToErrorMap } from "./tagged-error";
|
|
15
|
+
import type { EffectErrorMapToErrorMap } from "./types";
|
|
16
|
+
|
|
17
|
+
export const effectContractSymbol: unique symbol = Symbol.for(
|
|
18
|
+
"@orpc/effect/contract",
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
interface EffectContractMetadata<TEffectErrorMap extends EffectErrorMap> {
|
|
22
|
+
readonly [effectContractSymbol]: {
|
|
23
|
+
readonly errorMap: TEffectErrorMap;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type LocalEffectErrorMap<T> =
|
|
28
|
+
T extends EffectContractMetadata<infer TEffectErrorMap extends EffectErrorMap>
|
|
29
|
+
? TEffectErrorMap
|
|
30
|
+
: Record<never, never>;
|
|
31
|
+
|
|
32
|
+
type ContractWithEffectErrorMap<T, TEffectErrorMap extends EffectErrorMap> =
|
|
33
|
+
T extends ContractProcedure<
|
|
34
|
+
infer TInputSchema,
|
|
35
|
+
infer TOutputSchema,
|
|
36
|
+
infer TErrorMap extends ErrorMap,
|
|
37
|
+
infer TMeta extends Meta
|
|
38
|
+
>
|
|
39
|
+
? ContractProcedure<TInputSchema, TOutputSchema, TErrorMap, TMeta> &
|
|
40
|
+
EffectContractMetadata<
|
|
41
|
+
MergedEffectErrorMap<TEffectErrorMap, LocalEffectErrorMap<T>>
|
|
42
|
+
>
|
|
43
|
+
: T extends ContractRouter<Meta>
|
|
44
|
+
? {
|
|
45
|
+
[K in keyof T]: ContractWithEffectErrorMap<T[K], TEffectErrorMap>;
|
|
46
|
+
}
|
|
47
|
+
: never;
|
|
48
|
+
|
|
49
|
+
export interface EffectContractProcedureBuilder<
|
|
50
|
+
TInputSchema extends AnySchema,
|
|
51
|
+
TOutputSchema extends AnySchema,
|
|
52
|
+
TEffectErrorMap extends EffectErrorMap,
|
|
53
|
+
TMeta extends Meta,
|
|
54
|
+
>
|
|
55
|
+
extends
|
|
56
|
+
ContractProcedure<
|
|
57
|
+
TInputSchema,
|
|
58
|
+
TOutputSchema,
|
|
59
|
+
EffectErrorMapToErrorMap<TEffectErrorMap>,
|
|
60
|
+
TMeta
|
|
61
|
+
>,
|
|
62
|
+
EffectContractMetadata<TEffectErrorMap> {
|
|
63
|
+
errors<U extends EffectErrorMap>(
|
|
64
|
+
errors: U,
|
|
65
|
+
): EffectContractProcedureBuilder<
|
|
66
|
+
TInputSchema,
|
|
67
|
+
TOutputSchema,
|
|
68
|
+
MergedEffectErrorMap<TEffectErrorMap, U>,
|
|
69
|
+
TMeta
|
|
70
|
+
>;
|
|
71
|
+
meta(
|
|
72
|
+
meta: TMeta,
|
|
73
|
+
): EffectContractProcedureBuilder<
|
|
74
|
+
TInputSchema,
|
|
75
|
+
TOutputSchema,
|
|
76
|
+
TEffectErrorMap,
|
|
77
|
+
TMeta
|
|
78
|
+
>;
|
|
79
|
+
route(
|
|
80
|
+
route: Route,
|
|
81
|
+
): EffectContractProcedureBuilder<
|
|
82
|
+
TInputSchema,
|
|
83
|
+
TOutputSchema,
|
|
84
|
+
TEffectErrorMap,
|
|
85
|
+
TMeta
|
|
86
|
+
>;
|
|
87
|
+
input<U extends AnySchema>(
|
|
88
|
+
schema: U,
|
|
89
|
+
): EffectContractProcedureBuilderWithInput<
|
|
90
|
+
U,
|
|
91
|
+
TOutputSchema,
|
|
92
|
+
TEffectErrorMap,
|
|
93
|
+
TMeta
|
|
94
|
+
>;
|
|
95
|
+
output<U extends AnySchema>(
|
|
96
|
+
schema: U,
|
|
97
|
+
): EffectContractProcedureBuilderWithOutput<
|
|
98
|
+
TInputSchema,
|
|
99
|
+
U,
|
|
100
|
+
TEffectErrorMap,
|
|
101
|
+
TMeta
|
|
102
|
+
>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface EffectContractProcedureBuilderWithInput<
|
|
106
|
+
TInputSchema extends AnySchema,
|
|
107
|
+
TOutputSchema extends AnySchema,
|
|
108
|
+
TEffectErrorMap extends EffectErrorMap,
|
|
109
|
+
TMeta extends Meta,
|
|
110
|
+
>
|
|
111
|
+
extends
|
|
112
|
+
ContractProcedure<
|
|
113
|
+
TInputSchema,
|
|
114
|
+
TOutputSchema,
|
|
115
|
+
EffectErrorMapToErrorMap<TEffectErrorMap>,
|
|
116
|
+
TMeta
|
|
117
|
+
>,
|
|
118
|
+
EffectContractMetadata<TEffectErrorMap> {
|
|
119
|
+
errors<U extends EffectErrorMap>(
|
|
120
|
+
errors: U,
|
|
121
|
+
): EffectContractProcedureBuilderWithInput<
|
|
122
|
+
TInputSchema,
|
|
123
|
+
TOutputSchema,
|
|
124
|
+
MergedEffectErrorMap<TEffectErrorMap, U>,
|
|
125
|
+
TMeta
|
|
126
|
+
>;
|
|
127
|
+
meta(
|
|
128
|
+
meta: TMeta,
|
|
129
|
+
): EffectContractProcedureBuilderWithInput<
|
|
130
|
+
TInputSchema,
|
|
131
|
+
TOutputSchema,
|
|
132
|
+
TEffectErrorMap,
|
|
133
|
+
TMeta
|
|
134
|
+
>;
|
|
135
|
+
route(
|
|
136
|
+
route: Route,
|
|
137
|
+
): EffectContractProcedureBuilderWithInput<
|
|
138
|
+
TInputSchema,
|
|
139
|
+
TOutputSchema,
|
|
140
|
+
TEffectErrorMap,
|
|
141
|
+
TMeta
|
|
142
|
+
>;
|
|
143
|
+
output<U extends AnySchema>(
|
|
144
|
+
schema: U,
|
|
145
|
+
): EffectContractProcedureBuilderWithInputOutput<
|
|
146
|
+
TInputSchema,
|
|
147
|
+
U,
|
|
148
|
+
TEffectErrorMap,
|
|
149
|
+
TMeta
|
|
150
|
+
>;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface EffectContractProcedureBuilderWithOutput<
|
|
154
|
+
TInputSchema extends AnySchema,
|
|
155
|
+
TOutputSchema extends AnySchema,
|
|
156
|
+
TEffectErrorMap extends EffectErrorMap,
|
|
157
|
+
TMeta extends Meta,
|
|
158
|
+
>
|
|
159
|
+
extends
|
|
160
|
+
ContractProcedure<
|
|
161
|
+
TInputSchema,
|
|
162
|
+
TOutputSchema,
|
|
163
|
+
EffectErrorMapToErrorMap<TEffectErrorMap>,
|
|
164
|
+
TMeta
|
|
165
|
+
>,
|
|
166
|
+
EffectContractMetadata<TEffectErrorMap> {
|
|
167
|
+
errors<U extends EffectErrorMap>(
|
|
168
|
+
errors: U,
|
|
169
|
+
): EffectContractProcedureBuilderWithOutput<
|
|
170
|
+
TInputSchema,
|
|
171
|
+
TOutputSchema,
|
|
172
|
+
MergedEffectErrorMap<TEffectErrorMap, U>,
|
|
173
|
+
TMeta
|
|
174
|
+
>;
|
|
175
|
+
meta(
|
|
176
|
+
meta: TMeta,
|
|
177
|
+
): EffectContractProcedureBuilderWithOutput<
|
|
178
|
+
TInputSchema,
|
|
179
|
+
TOutputSchema,
|
|
180
|
+
TEffectErrorMap,
|
|
181
|
+
TMeta
|
|
182
|
+
>;
|
|
183
|
+
route(
|
|
184
|
+
route: Route,
|
|
185
|
+
): EffectContractProcedureBuilderWithOutput<
|
|
186
|
+
TInputSchema,
|
|
187
|
+
TOutputSchema,
|
|
188
|
+
TEffectErrorMap,
|
|
189
|
+
TMeta
|
|
190
|
+
>;
|
|
191
|
+
input<U extends AnySchema>(
|
|
192
|
+
schema: U,
|
|
193
|
+
): EffectContractProcedureBuilderWithInputOutput<
|
|
194
|
+
U,
|
|
195
|
+
TOutputSchema,
|
|
196
|
+
TEffectErrorMap,
|
|
197
|
+
TMeta
|
|
198
|
+
>;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface EffectContractProcedureBuilderWithInputOutput<
|
|
202
|
+
TInputSchema extends AnySchema,
|
|
203
|
+
TOutputSchema extends AnySchema,
|
|
204
|
+
TEffectErrorMap extends EffectErrorMap,
|
|
205
|
+
TMeta extends Meta,
|
|
206
|
+
>
|
|
207
|
+
extends
|
|
208
|
+
ContractProcedure<
|
|
209
|
+
TInputSchema,
|
|
210
|
+
TOutputSchema,
|
|
211
|
+
EffectErrorMapToErrorMap<TEffectErrorMap>,
|
|
212
|
+
TMeta
|
|
213
|
+
>,
|
|
214
|
+
EffectContractMetadata<TEffectErrorMap> {
|
|
215
|
+
errors<U extends EffectErrorMap>(
|
|
216
|
+
errors: U,
|
|
217
|
+
): EffectContractProcedureBuilderWithInputOutput<
|
|
218
|
+
TInputSchema,
|
|
219
|
+
TOutputSchema,
|
|
220
|
+
MergedEffectErrorMap<TEffectErrorMap, U>,
|
|
221
|
+
TMeta
|
|
222
|
+
>;
|
|
223
|
+
meta(
|
|
224
|
+
meta: TMeta,
|
|
225
|
+
): EffectContractProcedureBuilderWithInputOutput<
|
|
226
|
+
TInputSchema,
|
|
227
|
+
TOutputSchema,
|
|
228
|
+
TEffectErrorMap,
|
|
229
|
+
TMeta
|
|
230
|
+
>;
|
|
231
|
+
route(
|
|
232
|
+
route: Route,
|
|
233
|
+
): EffectContractProcedureBuilderWithInputOutput<
|
|
234
|
+
TInputSchema,
|
|
235
|
+
TOutputSchema,
|
|
236
|
+
TEffectErrorMap,
|
|
237
|
+
TMeta
|
|
238
|
+
>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export interface EffectContractRouterBuilder<
|
|
242
|
+
TEffectErrorMap extends EffectErrorMap,
|
|
243
|
+
TMeta extends Meta,
|
|
244
|
+
> extends EffectContractMetadata<TEffectErrorMap> {
|
|
245
|
+
errors<U extends EffectErrorMap>(
|
|
246
|
+
errors: U,
|
|
247
|
+
): EffectContractRouterBuilder<
|
|
248
|
+
MergedEffectErrorMap<TEffectErrorMap, U>,
|
|
249
|
+
TMeta
|
|
250
|
+
>;
|
|
251
|
+
prefix(prefix: HTTPPath): EffectContractRouterBuilder<TEffectErrorMap, TMeta>;
|
|
252
|
+
tag(...tags: string[]): EffectContractRouterBuilder<TEffectErrorMap, TMeta>;
|
|
253
|
+
router<T extends ContractRouter<TMeta>>(
|
|
254
|
+
router: T,
|
|
255
|
+
): ContractWithEffectErrorMap<T, TEffectErrorMap>;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface EffectContractBuilder<
|
|
259
|
+
TInputSchema extends AnySchema,
|
|
260
|
+
TOutputSchema extends AnySchema,
|
|
261
|
+
TEffectErrorMap extends EffectErrorMap,
|
|
262
|
+
TMeta extends Meta,
|
|
263
|
+
>
|
|
264
|
+
extends
|
|
265
|
+
ContractProcedure<
|
|
266
|
+
TInputSchema,
|
|
267
|
+
TOutputSchema,
|
|
268
|
+
EffectErrorMapToErrorMap<TEffectErrorMap>,
|
|
269
|
+
TMeta
|
|
270
|
+
>,
|
|
271
|
+
EffectContractMetadata<TEffectErrorMap> {
|
|
272
|
+
$meta<U extends Meta>(
|
|
273
|
+
initialMeta: U,
|
|
274
|
+
): EffectContractBuilder<TInputSchema, TOutputSchema, TEffectErrorMap, U>;
|
|
275
|
+
$route(
|
|
276
|
+
initialRoute: Route,
|
|
277
|
+
): EffectContractBuilder<TInputSchema, TOutputSchema, TEffectErrorMap, TMeta>;
|
|
278
|
+
$input<U extends AnySchema>(
|
|
279
|
+
initialInputSchema?: U,
|
|
280
|
+
): EffectContractBuilder<U, TOutputSchema, TEffectErrorMap, TMeta>;
|
|
281
|
+
errors<U extends EffectErrorMap>(
|
|
282
|
+
errors: U,
|
|
283
|
+
): EffectContractBuilder<
|
|
284
|
+
TInputSchema,
|
|
285
|
+
TOutputSchema,
|
|
286
|
+
MergedEffectErrorMap<TEffectErrorMap, U>,
|
|
287
|
+
TMeta
|
|
288
|
+
>;
|
|
289
|
+
meta(
|
|
290
|
+
meta: TMeta,
|
|
291
|
+
): EffectContractProcedureBuilder<
|
|
292
|
+
TInputSchema,
|
|
293
|
+
TOutputSchema,
|
|
294
|
+
TEffectErrorMap,
|
|
295
|
+
TMeta
|
|
296
|
+
>;
|
|
297
|
+
route(
|
|
298
|
+
route: Route,
|
|
299
|
+
): EffectContractProcedureBuilder<
|
|
300
|
+
TInputSchema,
|
|
301
|
+
TOutputSchema,
|
|
302
|
+
TEffectErrorMap,
|
|
303
|
+
TMeta
|
|
304
|
+
>;
|
|
305
|
+
input<U extends AnySchema>(
|
|
306
|
+
schema: U,
|
|
307
|
+
): EffectContractProcedureBuilderWithInput<
|
|
308
|
+
U,
|
|
309
|
+
TOutputSchema,
|
|
310
|
+
TEffectErrorMap,
|
|
311
|
+
TMeta
|
|
312
|
+
>;
|
|
313
|
+
output<U extends AnySchema>(
|
|
314
|
+
schema: U,
|
|
315
|
+
): EffectContractProcedureBuilderWithOutput<
|
|
316
|
+
TInputSchema,
|
|
317
|
+
U,
|
|
318
|
+
TEffectErrorMap,
|
|
319
|
+
TMeta
|
|
320
|
+
>;
|
|
321
|
+
prefix(prefix: HTTPPath): EffectContractRouterBuilder<TEffectErrorMap, TMeta>;
|
|
322
|
+
tag(...tags: string[]): EffectContractRouterBuilder<TEffectErrorMap, TMeta>;
|
|
323
|
+
router<T extends ContractRouter<TMeta>>(
|
|
324
|
+
router: T,
|
|
325
|
+
): ContractWithEffectErrorMap<T, TEffectErrorMap>;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function isWrappableContractBuilder(value: unknown): value is {
|
|
329
|
+
"~orpc": { errorMap: ErrorMap };
|
|
330
|
+
} {
|
|
331
|
+
return typeof value === "object" && value !== null && "~orpc" in value;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function mergeEffectErrorMaps(
|
|
335
|
+
left: EffectErrorMap | undefined,
|
|
336
|
+
right: EffectErrorMap | undefined,
|
|
337
|
+
): EffectErrorMap | undefined {
|
|
338
|
+
if (!left) {
|
|
339
|
+
return right;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (!right) {
|
|
343
|
+
return left;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
...left,
|
|
348
|
+
...right,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function setEffectContractErrorMap(
|
|
353
|
+
value: object,
|
|
354
|
+
effectErrorMap: EffectErrorMap | undefined,
|
|
355
|
+
): void {
|
|
356
|
+
if (!effectErrorMap) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
Object.defineProperty(value, effectContractSymbol, {
|
|
361
|
+
value: { errorMap: effectErrorMap },
|
|
362
|
+
enumerable: false,
|
|
363
|
+
configurable: true,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function getEffectContractErrorMap(
|
|
368
|
+
value: unknown,
|
|
369
|
+
): EffectErrorMap | undefined {
|
|
370
|
+
if (typeof value !== "object" || value === null) {
|
|
371
|
+
return undefined;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return (value as Partial<EffectContractMetadata<EffectErrorMap>>)[
|
|
375
|
+
effectContractSymbol
|
|
376
|
+
]?.errorMap;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function applyEffectContractErrorMapToRouter(
|
|
380
|
+
router: ContractRouter<Meta>,
|
|
381
|
+
source: ContractRouter<Meta> | undefined,
|
|
382
|
+
inheritedEffectErrorMap: EffectErrorMap | undefined,
|
|
383
|
+
): void {
|
|
384
|
+
const routerRecord = router as Record<string, ContractRouter<Meta>>;
|
|
385
|
+
const sourceRecord = source as
|
|
386
|
+
| Record<string, ContractRouter<Meta>>
|
|
387
|
+
| undefined;
|
|
388
|
+
|
|
389
|
+
for (const key of Object.keys(routerRecord)) {
|
|
390
|
+
const routerValue = routerRecord[key];
|
|
391
|
+
const sourceValue =
|
|
392
|
+
sourceRecord && typeof sourceRecord === "object"
|
|
393
|
+
? sourceRecord[key]
|
|
394
|
+
: undefined;
|
|
395
|
+
|
|
396
|
+
if (!routerValue) {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (isContractProcedure(routerValue)) {
|
|
401
|
+
const sourceEffectErrorMap = getEffectContractErrorMap(sourceValue);
|
|
402
|
+
setEffectContractErrorMap(
|
|
403
|
+
routerValue,
|
|
404
|
+
mergeEffectErrorMaps(inheritedEffectErrorMap, sourceEffectErrorMap),
|
|
405
|
+
);
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (typeof routerValue === "object") {
|
|
410
|
+
applyEffectContractErrorMapToRouter(
|
|
411
|
+
routerValue,
|
|
412
|
+
sourceValue as ContractRouter<Meta> | undefined,
|
|
413
|
+
inheritedEffectErrorMap,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function wrapEffectContractBuilder<T>(
|
|
420
|
+
builder: T,
|
|
421
|
+
inheritedEffectErrorMap?: EffectErrorMap,
|
|
422
|
+
): T {
|
|
423
|
+
const currentEffectErrorMap =
|
|
424
|
+
inheritedEffectErrorMap ?? getEffectContractErrorMap(builder);
|
|
425
|
+
|
|
426
|
+
if (typeof builder === "object" && builder !== null) {
|
|
427
|
+
setEffectContractErrorMap(builder as object, currentEffectErrorMap);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const proxy = new Proxy(builder as object, {
|
|
431
|
+
get(target, prop, receiver) {
|
|
432
|
+
if (prop === effectContractSymbol) {
|
|
433
|
+
return currentEffectErrorMap
|
|
434
|
+
? { errorMap: currentEffectErrorMap }
|
|
435
|
+
: undefined;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (prop === "errors") {
|
|
439
|
+
return (errors: EffectErrorMap) => {
|
|
440
|
+
const nextEffectErrorMap = mergeEffectErrorMaps(
|
|
441
|
+
currentEffectErrorMap,
|
|
442
|
+
errors,
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
return wrapEffectContractBuilder(
|
|
446
|
+
Reflect.apply(Reflect.get(target, prop, receiver), target, [
|
|
447
|
+
effectErrorMapToErrorMap(errors),
|
|
448
|
+
]),
|
|
449
|
+
nextEffectErrorMap,
|
|
450
|
+
);
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (prop === "router") {
|
|
455
|
+
return (router: ContractRouter<Meta>) => {
|
|
456
|
+
const result = Reflect.apply(
|
|
457
|
+
Reflect.get(target, prop, receiver),
|
|
458
|
+
target,
|
|
459
|
+
[router],
|
|
460
|
+
) as ContractRouter<Meta>;
|
|
461
|
+
|
|
462
|
+
applyEffectContractErrorMapToRouter(
|
|
463
|
+
result,
|
|
464
|
+
router,
|
|
465
|
+
currentEffectErrorMap,
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
return result;
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const value = Reflect.get(target, prop, receiver);
|
|
473
|
+
if (typeof value !== "function") {
|
|
474
|
+
return value;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return (...args: unknown[]) => {
|
|
478
|
+
const result = Reflect.apply(value, target, args);
|
|
479
|
+
return isWrappableContractBuilder(result)
|
|
480
|
+
? wrapEffectContractBuilder(result, currentEffectErrorMap)
|
|
481
|
+
: result;
|
|
482
|
+
};
|
|
483
|
+
},
|
|
484
|
+
}) as T;
|
|
485
|
+
|
|
486
|
+
setEffectContractErrorMap(proxy as object, currentEffectErrorMap);
|
|
487
|
+
|
|
488
|
+
return proxy;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
export const eoc = wrapEffectContractBuilder(
|
|
492
|
+
oc,
|
|
493
|
+
{},
|
|
494
|
+
) as unknown as EffectContractBuilder<
|
|
495
|
+
Schema<unknown, unknown>,
|
|
496
|
+
Schema<unknown, unknown>,
|
|
497
|
+
Record<never, never>,
|
|
498
|
+
Record<never, never>
|
|
499
|
+
>;
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
export { implementEffect } from "./contract";
|
|
2
|
+
export type {
|
|
3
|
+
EffectImplementer,
|
|
4
|
+
EffectImplementerInternal,
|
|
5
|
+
EffectProcedureImplementer,
|
|
6
|
+
} from "./contract";
|
|
7
|
+
export { eoc } from "./eoc";
|
|
8
|
+
export type {
|
|
9
|
+
EffectContractBuilder,
|
|
10
|
+
EffectContractProcedureBuilder,
|
|
11
|
+
EffectContractProcedureBuilderWithInput,
|
|
12
|
+
EffectContractProcedureBuilderWithInputOutput,
|
|
13
|
+
EffectContractProcedureBuilderWithOutput,
|
|
14
|
+
EffectContractRouterBuilder,
|
|
15
|
+
} from "./eoc";
|
|
1
16
|
export {
|
|
2
17
|
addSpanStackTrace,
|
|
3
18
|
EffectBuilder,
|
|
@@ -40,10 +55,10 @@ export type {
|
|
|
40
55
|
EffectProcedureHandler,
|
|
41
56
|
EffectRouterBuilder,
|
|
42
57
|
EffectSpanConfig,
|
|
43
|
-
InferBuilderInitialContext,
|
|
44
58
|
InferBuilderCurrentContext,
|
|
45
|
-
InferBuilderInputSchema,
|
|
46
|
-
InferBuilderOutputSchema,
|
|
47
59
|
InferBuilderErrorMap,
|
|
60
|
+
InferBuilderInitialContext,
|
|
61
|
+
InferBuilderInputSchema,
|
|
48
62
|
InferBuilderMeta,
|
|
63
|
+
InferBuilderOutputSchema,
|
|
49
64
|
} from "./types";
|
package/src/tagged-error.ts
CHANGED
|
@@ -15,7 +15,6 @@ import type {
|
|
|
15
15
|
ErrorMapItem,
|
|
16
16
|
InferSchemaOutput,
|
|
17
17
|
} from "@orpc/contract";
|
|
18
|
-
import type { ORPCErrorConstructorMap } from "@orpc/server";
|
|
19
18
|
import type { MaybeOptionalOptions } from "@orpc/shared";
|
|
20
19
|
import { resolveMaybeOptionalOptions } from "@orpc/shared";
|
|
21
20
|
import type { Pipeable } from "effect";
|
|
@@ -433,8 +432,29 @@ export type EffectErrorMapToUnion<T extends EffectErrorMap> = {
|
|
|
433
432
|
/**
|
|
434
433
|
* Constructor map for EffectErrorMap - provides typed error constructors for handlers.
|
|
435
434
|
*/
|
|
436
|
-
|
|
437
|
-
|
|
435
|
+
type EffectErrorConstructor<
|
|
436
|
+
TCode extends ORPCErrorCode,
|
|
437
|
+
TItem,
|
|
438
|
+
> = TItem extends AnyORPCTaggedErrorClass
|
|
439
|
+
? (...args: ConstructorParameters<TItem>) => InstanceType<TItem>
|
|
440
|
+
: TItem extends { data?: infer TSchema extends AnySchema }
|
|
441
|
+
? (
|
|
442
|
+
...rest: MaybeOptionalOptions<
|
|
443
|
+
Omit<
|
|
444
|
+
ORPCErrorOptions<InferSchemaOutput<TSchema>>,
|
|
445
|
+
"defined" | "status"
|
|
446
|
+
>
|
|
447
|
+
>
|
|
448
|
+
) => ORPCError<TCode, InferSchemaOutput<TSchema>>
|
|
449
|
+
: (
|
|
450
|
+
...rest: MaybeOptionalOptions<
|
|
451
|
+
Omit<ORPCErrorOptions<unknown>, "defined" | "status">
|
|
452
|
+
>
|
|
453
|
+
) => ORPCError<TCode, unknown>;
|
|
454
|
+
|
|
455
|
+
export type EffectErrorConstructorMap<T extends EffectErrorMap> = {
|
|
456
|
+
[K in Extract<keyof T, ORPCErrorCode>]: EffectErrorConstructor<K, T[K]>;
|
|
457
|
+
};
|
|
438
458
|
|
|
439
459
|
/**
|
|
440
460
|
* Creates an error constructor map from an EffectErrorMap.
|
|
@@ -478,7 +498,7 @@ export function createEffectErrorConstructorMap<T extends EffectErrorMap>(
|
|
|
478
498
|
},
|
|
479
499
|
});
|
|
480
500
|
|
|
481
|
-
return proxy as EffectErrorConstructorMap<T>;
|
|
501
|
+
return proxy as unknown as EffectErrorConstructorMap<T>;
|
|
482
502
|
}
|
|
483
503
|
|
|
484
504
|
/**
|