effect-orpc 0.1.3 → 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 +486 -0
- package/dist/index.js +352 -72
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- 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,249 @@
|
|
|
1
|
+
import type { Client, ORPCError } from "@orpc/client";
|
|
2
|
+
import type { ErrorFromErrorMap } from "@orpc/contract";
|
|
3
|
+
import type {
|
|
4
|
+
ActionableClient,
|
|
5
|
+
ActionableError,
|
|
6
|
+
ImplementedProcedure,
|
|
7
|
+
Middleware,
|
|
8
|
+
MiddlewareOutputFn,
|
|
9
|
+
ProcedureImplementer,
|
|
10
|
+
} from "@orpc/server";
|
|
11
|
+
import { describe, expectTypeOf, it } from "vitest";
|
|
12
|
+
|
|
13
|
+
import { implementEffect } from "../index";
|
|
14
|
+
import type { InitialContext } from "./parity-shared";
|
|
15
|
+
import { runtime, typedContract } from "./parity-shared";
|
|
16
|
+
import {
|
|
17
|
+
type AssertExtends,
|
|
18
|
+
baseErrorMap,
|
|
19
|
+
type BaseMeta,
|
|
20
|
+
inputSchema,
|
|
21
|
+
outputSchema,
|
|
22
|
+
} from "./shared";
|
|
23
|
+
|
|
24
|
+
const implementer = implementEffect(typedContract, runtime)
|
|
25
|
+
.$context<InitialContext>()
|
|
26
|
+
.use(({ next }) => next({ context: { auth: true as boolean } }));
|
|
27
|
+
|
|
28
|
+
describe("parity: @orpc/server implementer-procedure.test-d.ts", () => {
|
|
29
|
+
const builder = implementer.ping;
|
|
30
|
+
|
|
31
|
+
it("backward compatibility", () => {
|
|
32
|
+
expectTypeOf<keyof typeof builder>().toEqualTypeOf<
|
|
33
|
+
"~orpc" | "handler" | "use" | "effect"
|
|
34
|
+
>();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("is a contract procedure", () => {
|
|
38
|
+
expectTypeOf(builder["~orpc"]).toBeObject();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("extends the matching upstream implementer interface", () => {
|
|
42
|
+
expectTypeOf<
|
|
43
|
+
AssertExtends<
|
|
44
|
+
typeof builder,
|
|
45
|
+
ProcedureImplementer<
|
|
46
|
+
InitialContext & Record<never, never>,
|
|
47
|
+
InitialContext & { auth: boolean },
|
|
48
|
+
typeof inputSchema,
|
|
49
|
+
typeof outputSchema,
|
|
50
|
+
typeof baseErrorMap,
|
|
51
|
+
BaseMeta
|
|
52
|
+
>
|
|
53
|
+
>
|
|
54
|
+
>().toEqualTypeOf<true>();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe(".use", () => {
|
|
58
|
+
it("without map input", () => {
|
|
59
|
+
expectTypeOf(
|
|
60
|
+
builder.use(
|
|
61
|
+
(
|
|
62
|
+
{ context, next, path, procedure, errors, signal },
|
|
63
|
+
input,
|
|
64
|
+
output,
|
|
65
|
+
) => {
|
|
66
|
+
expectTypeOf(input).toEqualTypeOf<{ input: string }>();
|
|
67
|
+
expectTypeOf(context).toEqualTypeOf<
|
|
68
|
+
InitialContext & { auth: boolean }
|
|
69
|
+
>();
|
|
70
|
+
expectTypeOf(path).toExtend<readonly string[]>();
|
|
71
|
+
expectTypeOf(procedure).toBeObject();
|
|
72
|
+
expectTypeOf(output).toBeFunction();
|
|
73
|
+
expectTypeOf<
|
|
74
|
+
AssertExtends<
|
|
75
|
+
ReturnType<typeof errors.BASE>,
|
|
76
|
+
ORPCError<"BASE", { output: number }>
|
|
77
|
+
>
|
|
78
|
+
>().toEqualTypeOf<true>();
|
|
79
|
+
expectTypeOf<
|
|
80
|
+
AssertExtends<
|
|
81
|
+
ReturnType<typeof errors.OVERRIDE>,
|
|
82
|
+
ORPCError<"OVERRIDE", any>
|
|
83
|
+
>
|
|
84
|
+
>().toEqualTypeOf<true>();
|
|
85
|
+
expectTypeOf(signal).toExtend<AbortSignal | undefined>();
|
|
86
|
+
|
|
87
|
+
return next({ context: { extra: true } });
|
|
88
|
+
},
|
|
89
|
+
),
|
|
90
|
+
).toBeObject();
|
|
91
|
+
|
|
92
|
+
builder.use(
|
|
93
|
+
// @ts-expect-error - invalid TInContext
|
|
94
|
+
{} as Middleware<{ auth: "invalid" }, any, any, any, any, any>,
|
|
95
|
+
);
|
|
96
|
+
// @ts-expect-error - input is not match
|
|
97
|
+
builder.use(({ next }, _input: "invalid") => next({}));
|
|
98
|
+
// @ts-expect-error - output is not match
|
|
99
|
+
builder.use(({ next }, _input, _output: MiddlewareOutputFn<"invalid">) =>
|
|
100
|
+
next({}),
|
|
101
|
+
);
|
|
102
|
+
// @ts-expect-error - conflict context
|
|
103
|
+
builder.use(({ next }) => next({ context: { db: undefined } }));
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("with map input", () => {
|
|
107
|
+
expectTypeOf(
|
|
108
|
+
builder.use(
|
|
109
|
+
(
|
|
110
|
+
{ context, next, path, procedure, errors, signal },
|
|
111
|
+
input: { mapped: boolean },
|
|
112
|
+
output,
|
|
113
|
+
) => {
|
|
114
|
+
expectTypeOf(input).toEqualTypeOf<{ mapped: boolean }>();
|
|
115
|
+
expectTypeOf(context).toEqualTypeOf<
|
|
116
|
+
InitialContext & { auth: boolean }
|
|
117
|
+
>();
|
|
118
|
+
expectTypeOf(path).toExtend<readonly string[]>();
|
|
119
|
+
expectTypeOf(procedure).toBeObject();
|
|
120
|
+
expectTypeOf(output).toBeFunction();
|
|
121
|
+
expectTypeOf<
|
|
122
|
+
AssertExtends<
|
|
123
|
+
ReturnType<typeof errors.BASE>,
|
|
124
|
+
ORPCError<"BASE", { output: number }>
|
|
125
|
+
>
|
|
126
|
+
>().toEqualTypeOf<true>();
|
|
127
|
+
expectTypeOf<
|
|
128
|
+
AssertExtends<
|
|
129
|
+
ReturnType<typeof errors.OVERRIDE>,
|
|
130
|
+
ORPCError<"OVERRIDE", any>
|
|
131
|
+
>
|
|
132
|
+
>().toEqualTypeOf<true>();
|
|
133
|
+
expectTypeOf(signal).toExtend<AbortSignal | undefined>();
|
|
134
|
+
|
|
135
|
+
return next({ context: { extra: true } });
|
|
136
|
+
},
|
|
137
|
+
(input) => {
|
|
138
|
+
expectTypeOf(input).toEqualTypeOf<{ input: string }>();
|
|
139
|
+
return { mapped: true };
|
|
140
|
+
},
|
|
141
|
+
),
|
|
142
|
+
).toBeObject();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("with TInContext", () => {
|
|
146
|
+
const mid = {} as Middleware<
|
|
147
|
+
{ cacheable?: boolean } & Record<never, never>,
|
|
148
|
+
Record<never, never>,
|
|
149
|
+
unknown,
|
|
150
|
+
any,
|
|
151
|
+
any,
|
|
152
|
+
BaseMeta
|
|
153
|
+
>;
|
|
154
|
+
|
|
155
|
+
expectTypeOf(builder.use(mid)).toBeObject();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it(".handler returns an implemented procedure", () => {
|
|
160
|
+
const handled = builder.handler(
|
|
161
|
+
({ input, context, procedure, path, signal, errors }) => {
|
|
162
|
+
expectTypeOf(input).toEqualTypeOf<{ input: string }>();
|
|
163
|
+
expectTypeOf(context).toEqualTypeOf<
|
|
164
|
+
InitialContext & { auth: boolean }
|
|
165
|
+
>();
|
|
166
|
+
expectTypeOf(procedure).toBeObject();
|
|
167
|
+
expectTypeOf(path).toExtend<readonly string[]>();
|
|
168
|
+
expectTypeOf(signal).toExtend<AbortSignal | undefined>();
|
|
169
|
+
expectTypeOf<
|
|
170
|
+
AssertExtends<
|
|
171
|
+
ReturnType<typeof errors.BASE>,
|
|
172
|
+
ORPCError<"BASE", { output: number }>
|
|
173
|
+
>
|
|
174
|
+
>().toEqualTypeOf<true>();
|
|
175
|
+
expectTypeOf<
|
|
176
|
+
AssertExtends<
|
|
177
|
+
ReturnType<typeof errors.OVERRIDE>,
|
|
178
|
+
ORPCError<"OVERRIDE", any>
|
|
179
|
+
>
|
|
180
|
+
>().toEqualTypeOf<true>();
|
|
181
|
+
|
|
182
|
+
return { output: 456 };
|
|
183
|
+
},
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
expectTypeOf<
|
|
187
|
+
AssertExtends<
|
|
188
|
+
typeof handled,
|
|
189
|
+
ImplementedProcedure<
|
|
190
|
+
InitialContext & Record<never, never>,
|
|
191
|
+
InitialContext & { auth: boolean },
|
|
192
|
+
typeof inputSchema,
|
|
193
|
+
typeof outputSchema,
|
|
194
|
+
typeof baseErrorMap,
|
|
195
|
+
BaseMeta
|
|
196
|
+
>
|
|
197
|
+
>
|
|
198
|
+
>().toEqualTypeOf<true>();
|
|
199
|
+
|
|
200
|
+
expectTypeOf(handled.use).toBeFunction();
|
|
201
|
+
expectTypeOf(handled.callable).toBeFunction();
|
|
202
|
+
expectTypeOf(handled.actionable).toBeFunction();
|
|
203
|
+
const callable = handled.callable({
|
|
204
|
+
context: async (_clientContext: { batch?: boolean }) => ({
|
|
205
|
+
db: "postgres",
|
|
206
|
+
}),
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
expectTypeOf<
|
|
210
|
+
AssertExtends<
|
|
211
|
+
typeof callable,
|
|
212
|
+
Client<
|
|
213
|
+
{ batch?: boolean },
|
|
214
|
+
{ input: number },
|
|
215
|
+
{ output: string },
|
|
216
|
+
ErrorFromErrorMap<typeof baseErrorMap>
|
|
217
|
+
>
|
|
218
|
+
>
|
|
219
|
+
>().toEqualTypeOf<true>();
|
|
220
|
+
|
|
221
|
+
const actionable = handled.actionable({
|
|
222
|
+
context: async (_clientContext: { batch?: boolean }) => ({
|
|
223
|
+
db: "postgres",
|
|
224
|
+
}),
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
expectTypeOf<
|
|
228
|
+
AssertExtends<
|
|
229
|
+
typeof actionable,
|
|
230
|
+
ActionableClient<
|
|
231
|
+
{ input: number },
|
|
232
|
+
{ output: string },
|
|
233
|
+
ActionableError<ErrorFromErrorMap<typeof baseErrorMap>>
|
|
234
|
+
>
|
|
235
|
+
>
|
|
236
|
+
>().toEqualTypeOf<true>();
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe("parity: @orpc/server implementer-variants.test-d.ts", () => {
|
|
241
|
+
it("router-level .use returns an internal implementer", () => {
|
|
242
|
+
const applied = implementer.nested.use(({ next }) =>
|
|
243
|
+
next({ context: { extra: true } }),
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
expectTypeOf(applied).toBeObject();
|
|
247
|
+
expectTypeOf(applied.ping.handler).toBeFunction();
|
|
248
|
+
});
|
|
249
|
+
});
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import type { ORPCError } from "@orpc/client";
|
|
2
|
+
import type { Meta, Schema } from "@orpc/contract";
|
|
3
|
+
import type { Middleware, Router } from "@orpc/server";
|
|
4
|
+
import { describe, expect, expectTypeOf, it } from "vitest";
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
EffectImplementer,
|
|
8
|
+
EffectImplementerInternal,
|
|
9
|
+
EffectProcedureImplementer,
|
|
10
|
+
} from "../index";
|
|
11
|
+
import { implementEffect } from "../index";
|
|
12
|
+
import type { CurrentContext, InitialContext } from "./parity-shared";
|
|
13
|
+
import { runtime, typedContract } from "./parity-shared";
|
|
14
|
+
import type { AssertExtends } from "./shared";
|
|
15
|
+
import {
|
|
16
|
+
baseErrorMap,
|
|
17
|
+
type BaseMeta,
|
|
18
|
+
inputSchema,
|
|
19
|
+
outputSchema,
|
|
20
|
+
} from "./shared";
|
|
21
|
+
|
|
22
|
+
const rootImplementer = implementEffect(typedContract, runtime);
|
|
23
|
+
const implementer = rootImplementer
|
|
24
|
+
.$context<InitialContext>()
|
|
25
|
+
.use(({ next }) => next({ context: { auth: true as boolean } }));
|
|
26
|
+
|
|
27
|
+
describe("parity: @orpc/server implementer.test-d.ts", () => {
|
|
28
|
+
describe("root level", () => {
|
|
29
|
+
it(".$context", () => {
|
|
30
|
+
expectTypeOf(rootImplementer.$context<{ anything: string }>()).toExtend<
|
|
31
|
+
EffectImplementer<
|
|
32
|
+
typeof typedContract,
|
|
33
|
+
{ anything: string } & Record<never, never>,
|
|
34
|
+
{ anything: string },
|
|
35
|
+
never,
|
|
36
|
+
never
|
|
37
|
+
>
|
|
38
|
+
>();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it(".$config", () => {
|
|
42
|
+
expectTypeOf(
|
|
43
|
+
rootImplementer.$config({
|
|
44
|
+
initialInputValidationIndex: Number.NEGATIVE_INFINITY,
|
|
45
|
+
}),
|
|
46
|
+
).toExtend<
|
|
47
|
+
EffectImplementer<
|
|
48
|
+
typeof typedContract,
|
|
49
|
+
Record<never, never>,
|
|
50
|
+
Record<never, never>,
|
|
51
|
+
never,
|
|
52
|
+
never
|
|
53
|
+
>
|
|
54
|
+
>();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("router level", () => {
|
|
59
|
+
it(".middleware", () => {
|
|
60
|
+
expectTypeOf(
|
|
61
|
+
rootImplementer
|
|
62
|
+
.$context<InitialContext>()
|
|
63
|
+
.nested.middleware(
|
|
64
|
+
(
|
|
65
|
+
{ context, next, path, procedure, errors, signal },
|
|
66
|
+
input,
|
|
67
|
+
output,
|
|
68
|
+
) => {
|
|
69
|
+
expectTypeOf(context).toExtend<InitialContext>();
|
|
70
|
+
expectTypeOf(input).toEqualTypeOf<unknown>();
|
|
71
|
+
expectTypeOf(path).toExtend<readonly string[]>();
|
|
72
|
+
expectTypeOf(procedure).toBeObject();
|
|
73
|
+
expectTypeOf(output).toBeFunction();
|
|
74
|
+
expectTypeOf(errors).toBeObject();
|
|
75
|
+
expectTypeOf(signal).toExtend<AbortSignal | undefined>();
|
|
76
|
+
|
|
77
|
+
return next({ context: { extra: true } });
|
|
78
|
+
},
|
|
79
|
+
),
|
|
80
|
+
).toBeObject();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it(".use", () => {
|
|
84
|
+
expectTypeOf(
|
|
85
|
+
implementer.nested.use(
|
|
86
|
+
(
|
|
87
|
+
{ context, next, path, procedure, errors, signal },
|
|
88
|
+
input,
|
|
89
|
+
output,
|
|
90
|
+
) => {
|
|
91
|
+
expectTypeOf(context).toEqualTypeOf<CurrentContext>();
|
|
92
|
+
expectTypeOf(input).toEqualTypeOf<unknown>();
|
|
93
|
+
expectTypeOf(path).toExtend<readonly string[]>();
|
|
94
|
+
expectTypeOf(procedure).toBeObject();
|
|
95
|
+
expectTypeOf(output).toBeFunction();
|
|
96
|
+
expectTypeOf(errors).toBeObject();
|
|
97
|
+
expectTypeOf(signal).toExtend<AbortSignal | undefined>();
|
|
98
|
+
|
|
99
|
+
return next({ context: { extra: true } });
|
|
100
|
+
},
|
|
101
|
+
),
|
|
102
|
+
).toBeObject();
|
|
103
|
+
|
|
104
|
+
const mid = {} as Middleware<
|
|
105
|
+
{ cacheable?: boolean } & Record<never, never>,
|
|
106
|
+
Record<never, never>,
|
|
107
|
+
unknown,
|
|
108
|
+
unknown,
|
|
109
|
+
any,
|
|
110
|
+
BaseMeta
|
|
111
|
+
>;
|
|
112
|
+
|
|
113
|
+
expectTypeOf(implementer.use(mid)).toExtend<
|
|
114
|
+
EffectImplementerInternal<
|
|
115
|
+
typeof typedContract,
|
|
116
|
+
InitialContext & { cacheable?: boolean },
|
|
117
|
+
Omit<CurrentContext, never> & Record<never, never>,
|
|
118
|
+
never,
|
|
119
|
+
never
|
|
120
|
+
>
|
|
121
|
+
>();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it(".router / .lazy", () => {
|
|
125
|
+
const implementedRouter = {
|
|
126
|
+
ping: implementer.ping.handler(({ input }) => ({
|
|
127
|
+
output: Number(input.input),
|
|
128
|
+
})),
|
|
129
|
+
pong: implementer.pong.handler(() => undefined),
|
|
130
|
+
nested: {
|
|
131
|
+
ping: implementer.nested.ping.handler(({ input }) => ({
|
|
132
|
+
output: Number(input.input),
|
|
133
|
+
})),
|
|
134
|
+
pong: implementer.nested.pong.handler(() => undefined),
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
const router = implementer.router(implementedRouter);
|
|
138
|
+
|
|
139
|
+
expectTypeOf<
|
|
140
|
+
AssertExtends<
|
|
141
|
+
typeof router,
|
|
142
|
+
Router<typeof typedContract, CurrentContext>
|
|
143
|
+
>
|
|
144
|
+
>().toEqualTypeOf<true>();
|
|
145
|
+
|
|
146
|
+
expectTypeOf(
|
|
147
|
+
implementer.lazy(async () => ({ default: implementedRouter })),
|
|
148
|
+
).toBeObject();
|
|
149
|
+
|
|
150
|
+
// @ts-expect-error - meta def is not match
|
|
151
|
+
implementer.router({ ping: {} });
|
|
152
|
+
|
|
153
|
+
// @ts-expect-error - missing implementation
|
|
154
|
+
implementer.router({ ping: implementedRouter.ping });
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("each procedure is a ProcedureImplementer", () => {
|
|
159
|
+
type ExpectedPing = EffectProcedureImplementer<
|
|
160
|
+
InitialContext & Record<never, never>,
|
|
161
|
+
CurrentContext,
|
|
162
|
+
typeof inputSchema,
|
|
163
|
+
typeof outputSchema,
|
|
164
|
+
typeof baseErrorMap,
|
|
165
|
+
BaseMeta,
|
|
166
|
+
never,
|
|
167
|
+
never
|
|
168
|
+
>;
|
|
169
|
+
|
|
170
|
+
type ExpectedPong = EffectProcedureImplementer<
|
|
171
|
+
InitialContext & Record<never, never>,
|
|
172
|
+
CurrentContext,
|
|
173
|
+
Schema<unknown, unknown>,
|
|
174
|
+
Schema<unknown, unknown>,
|
|
175
|
+
Record<never, never>,
|
|
176
|
+
Meta,
|
|
177
|
+
never,
|
|
178
|
+
never
|
|
179
|
+
>;
|
|
180
|
+
|
|
181
|
+
expectTypeOf(implementer.ping).toExtend<ExpectedPing>();
|
|
182
|
+
expectTypeOf(implementer.nested.ping).toExtend<ExpectedPing>();
|
|
183
|
+
expectTypeOf(implementer.pong).toExtend<ExpectedPong>();
|
|
184
|
+
expectTypeOf(implementer.nested.pong).toExtend<ExpectedPong>();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("procedure .use preserves the leaf error map", () => {
|
|
188
|
+
implementer.ping.use(({ errors, next }) => {
|
|
189
|
+
expectTypeOf<
|
|
190
|
+
AssertExtends<
|
|
191
|
+
ReturnType<typeof errors.BASE>,
|
|
192
|
+
ORPCError<"BASE", { output: number }>
|
|
193
|
+
>
|
|
194
|
+
>().toEqualTypeOf<true>();
|
|
195
|
+
expectTypeOf<
|
|
196
|
+
AssertExtends<
|
|
197
|
+
ReturnType<typeof errors.OVERRIDE>,
|
|
198
|
+
ORPCError<"OVERRIDE", any>
|
|
199
|
+
>
|
|
200
|
+
>().toEqualTypeOf<true>();
|
|
201
|
+
// @ts-expect-error - leaf middleware errors should not widen to arbitrary keys
|
|
202
|
+
expect(errors.MISSING).toBeUndefined();
|
|
203
|
+
|
|
204
|
+
return next({});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
implementer.ping.use(
|
|
208
|
+
({ errors, next }) => {
|
|
209
|
+
expectTypeOf<
|
|
210
|
+
AssertExtends<
|
|
211
|
+
ReturnType<typeof errors.BASE>,
|
|
212
|
+
ORPCError<"BASE", { output: number }>
|
|
213
|
+
>
|
|
214
|
+
>().toEqualTypeOf<true>();
|
|
215
|
+
expectTypeOf<
|
|
216
|
+
AssertExtends<
|
|
217
|
+
ReturnType<typeof errors.OVERRIDE>,
|
|
218
|
+
ORPCError<"OVERRIDE", any>
|
|
219
|
+
>
|
|
220
|
+
>().toEqualTypeOf<true>();
|
|
221
|
+
// @ts-expect-error - mapped input overload should keep the same leaf error map
|
|
222
|
+
expect(errors.MISSING).toBeUndefined();
|
|
223
|
+
|
|
224
|
+
return next({});
|
|
225
|
+
},
|
|
226
|
+
() => ({ mapped: true }),
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("procedure .handler preserves upstream handler option typing", () => {
|
|
231
|
+
implementer.ping.handler(
|
|
232
|
+
({ context, input, path, procedure, errors, signal }) => {
|
|
233
|
+
expectTypeOf(context).toExtend<CurrentContext>();
|
|
234
|
+
expectTypeOf(input).toEqualTypeOf<{ input: string }>();
|
|
235
|
+
expectTypeOf(path).toExtend<readonly string[]>();
|
|
236
|
+
expectTypeOf(procedure).toBeObject();
|
|
237
|
+
expectTypeOf(signal).toExtend<AbortSignal | undefined>();
|
|
238
|
+
expectTypeOf<
|
|
239
|
+
AssertExtends<
|
|
240
|
+
ReturnType<typeof errors.BASE>,
|
|
241
|
+
ORPCError<"BASE", { output: number }>
|
|
242
|
+
>
|
|
243
|
+
>().toEqualTypeOf<true>();
|
|
244
|
+
expectTypeOf<
|
|
245
|
+
AssertExtends<
|
|
246
|
+
ReturnType<typeof errors.OVERRIDE>,
|
|
247
|
+
ORPCError<"OVERRIDE", any>
|
|
248
|
+
>
|
|
249
|
+
>().toEqualTypeOf<true>();
|
|
250
|
+
// @ts-expect-error - handler errors should not widen beyond the leaf error map
|
|
251
|
+
expect(errors.MISSING).toBeUndefined();
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
output: Number(input.input),
|
|
255
|
+
};
|
|
256
|
+
},
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
implementer.ping.handler(
|
|
260
|
+
// @ts-expect-error - handler output must match the contract output shape
|
|
261
|
+
() => ({ output: "wrong" }),
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
implementer.ping.handler(
|
|
265
|
+
// @ts-expect-error - handler input should be the parsed contract input
|
|
266
|
+
({ input }: { input: { invalid: true } }) => ({
|
|
267
|
+
output: Number(input.invalid),
|
|
268
|
+
}),
|
|
269
|
+
);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it("procedure leaves do not expose .middleware", () => {
|
|
273
|
+
expect(
|
|
274
|
+
(implementer.ping as { middleware?: unknown }).middleware,
|
|
275
|
+
).toBeUndefined();
|
|
276
|
+
// @ts-expect-error - procedure implementer leaves should not expose middleware
|
|
277
|
+
expect(implementer.ping.middleware).toBeUndefined();
|
|
278
|
+
expectTypeOf(implementer.nested).toHaveProperty("middleware");
|
|
279
|
+
});
|
|
280
|
+
});
|
package/src/tests/shared.ts
CHANGED
package/src/types/index.ts
CHANGED
|
@@ -22,7 +22,6 @@ import type {
|
|
|
22
22
|
ProcedureHandlerOptions,
|
|
23
23
|
RouterBuilder,
|
|
24
24
|
} from "@orpc/server";
|
|
25
|
-
import type { Promisable } from "@orpc/shared";
|
|
26
25
|
import type { Effect, ManagedRuntime } from "effect";
|
|
27
26
|
import type { YieldWrap } from "effect/Utils";
|
|
28
27
|
|
|
@@ -62,21 +61,6 @@ export interface EffectBuilderDef<
|
|
|
62
61
|
effectErrorMap: TEffectErrorMap;
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
export type NonEffectProcedureHandler<
|
|
66
|
-
TCurrentContext extends Context,
|
|
67
|
-
TInput,
|
|
68
|
-
THandlerOutput,
|
|
69
|
-
TEffectErrorMap extends EffectErrorMap,
|
|
70
|
-
TMeta extends Meta,
|
|
71
|
-
> = (
|
|
72
|
-
opt: ProcedureHandlerOptions<
|
|
73
|
-
TCurrentContext,
|
|
74
|
-
TInput,
|
|
75
|
-
EffectErrorConstructorMap<TEffectErrorMap>,
|
|
76
|
-
TMeta
|
|
77
|
-
>,
|
|
78
|
-
) => Promisable<THandlerOutput>;
|
|
79
|
-
|
|
80
64
|
/**
|
|
81
65
|
* Extended procedure definition that includes the Effect ManagedRuntime.
|
|
82
66
|
*/
|
package/src/types/variants.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
MergedCurrentContext,
|
|
22
22
|
MergedInitialContext,
|
|
23
23
|
Middleware,
|
|
24
|
+
ProcedureHandler,
|
|
24
25
|
Router,
|
|
25
26
|
} from "@orpc/server";
|
|
26
27
|
|
|
@@ -201,12 +202,11 @@ export interface EffectBuilderWithMiddlewares<
|
|
|
201
202
|
* @see {@link https://orpc.dev/docs/procedure Procedure Docs}
|
|
202
203
|
*/
|
|
203
204
|
"handler"<UFuncOutput>(
|
|
204
|
-
handler:
|
|
205
|
+
handler: ProcedureHandler<
|
|
205
206
|
TCurrentContext,
|
|
206
207
|
unknown,
|
|
207
208
|
UFuncOutput,
|
|
208
|
-
TEffectErrorMap
|
|
209
|
-
TRequirementsProvided,
|
|
209
|
+
EffectErrorMapToErrorMap<TEffectErrorMap>,
|
|
210
210
|
TMeta
|
|
211
211
|
>,
|
|
212
212
|
): EffectDecoratedProcedure<
|
|
@@ -476,12 +476,11 @@ export interface EffectProcedureBuilder<
|
|
|
476
476
|
* @see {@link https://orpc.dev/docs/procedure Procedure Docs}
|
|
477
477
|
*/
|
|
478
478
|
"handler"<UFuncOutput>(
|
|
479
|
-
handler:
|
|
479
|
+
handler: ProcedureHandler<
|
|
480
480
|
TCurrentContext,
|
|
481
481
|
unknown,
|
|
482
482
|
UFuncOutput,
|
|
483
|
-
TEffectErrorMap
|
|
484
|
-
TRequirementsProvided,
|
|
483
|
+
EffectErrorMapToErrorMap<TEffectErrorMap>,
|
|
485
484
|
TMeta
|
|
486
485
|
>,
|
|
487
486
|
): EffectDecoratedProcedure<
|
|
@@ -514,6 +513,19 @@ export interface EffectProcedureBuilder<
|
|
|
514
513
|
TRequirementsProvided,
|
|
515
514
|
TRuntimeError
|
|
516
515
|
>;
|
|
516
|
+
|
|
517
|
+
"traced"(
|
|
518
|
+
spanName: string,
|
|
519
|
+
): EffectProcedureBuilder<
|
|
520
|
+
TInitialContext,
|
|
521
|
+
TCurrentContext,
|
|
522
|
+
TInputSchema,
|
|
523
|
+
TOutputSchema,
|
|
524
|
+
TEffectErrorMap,
|
|
525
|
+
TMeta,
|
|
526
|
+
TRequirementsProvided,
|
|
527
|
+
TRuntimeError
|
|
528
|
+
>;
|
|
517
529
|
}
|
|
518
530
|
|
|
519
531
|
export interface EffectProcedureBuilderWithInput<
|
|
@@ -694,12 +706,11 @@ export interface EffectProcedureBuilderWithInput<
|
|
|
694
706
|
* @see {@link https://orpc.dev/docs/procedure Procedure Docs}
|
|
695
707
|
*/
|
|
696
708
|
"handler"<UFuncOutput>(
|
|
697
|
-
handler:
|
|
709
|
+
handler: ProcedureHandler<
|
|
698
710
|
TCurrentContext,
|
|
699
711
|
InferSchemaOutput<TInputSchema>,
|
|
700
712
|
UFuncOutput,
|
|
701
|
-
TEffectErrorMap
|
|
702
|
-
TRequirementsProvided,
|
|
713
|
+
EffectErrorMapToErrorMap<TEffectErrorMap>,
|
|
703
714
|
TMeta
|
|
704
715
|
>,
|
|
705
716
|
): EffectDecoratedProcedure<
|
|
@@ -791,7 +802,7 @@ export interface EffectProcedureBuilderWithOutput<
|
|
|
791
802
|
TCurrentContext,
|
|
792
803
|
TInputSchema,
|
|
793
804
|
TOutputSchema,
|
|
794
|
-
TEffectErrorMap,
|
|
805
|
+
MergedEffectErrorMap<TEffectErrorMap, U>,
|
|
795
806
|
TMeta,
|
|
796
807
|
TRequirementsProvided,
|
|
797
808
|
TRuntimeError
|
|
@@ -891,12 +902,11 @@ export interface EffectProcedureBuilderWithOutput<
|
|
|
891
902
|
* @see {@link https://orpc.dev/docs/procedure Procedure Docs}
|
|
892
903
|
*/
|
|
893
904
|
"handler"(
|
|
894
|
-
handler:
|
|
905
|
+
handler: ProcedureHandler<
|
|
895
906
|
TCurrentContext,
|
|
896
907
|
unknown,
|
|
897
908
|
InferSchemaInput<TOutputSchema>,
|
|
898
|
-
TEffectErrorMap
|
|
899
|
-
TRequirementsProvided,
|
|
909
|
+
EffectErrorMapToErrorMap<TEffectErrorMap>,
|
|
900
910
|
TMeta
|
|
901
911
|
>,
|
|
902
912
|
): EffectDecoratedProcedure<
|
|
@@ -1104,12 +1114,11 @@ export interface EffectProcedureBuilderWithInputOutput<
|
|
|
1104
1114
|
* @see {@link https://orpc.dev/docs/procedure Procedure Docs}
|
|
1105
1115
|
*/
|
|
1106
1116
|
"handler"(
|
|
1107
|
-
handler:
|
|
1117
|
+
handler: ProcedureHandler<
|
|
1108
1118
|
TCurrentContext,
|
|
1109
1119
|
InferSchemaOutput<TInputSchema>,
|
|
1110
1120
|
InferSchemaInput<TOutputSchema>,
|
|
1111
|
-
TEffectErrorMap
|
|
1112
|
-
TRequirementsProvided,
|
|
1121
|
+
EffectErrorMapToErrorMap<TEffectErrorMap>,
|
|
1113
1122
|
TMeta
|
|
1114
1123
|
>,
|
|
1115
1124
|
): EffectDecoratedProcedure<
|