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,346 @@
|
|
|
1
|
+
import { oc, type InferSchemaOutput } from "@orpc/contract";
|
|
2
|
+
import { call, ORPCError, type Router } from "@orpc/server";
|
|
3
|
+
import { Effect, FiberRef, Layer, ManagedRuntime } from "effect";
|
|
4
|
+
import { describe, expect, expectTypeOf, it } from "vitest";
|
|
5
|
+
import z from "zod";
|
|
6
|
+
|
|
7
|
+
import { eoc, implementEffect, ORPCTaggedError } from "../index";
|
|
8
|
+
import { withFiberContext } from "../node";
|
|
9
|
+
|
|
10
|
+
class Counter extends Effect.Tag("Counter")<
|
|
11
|
+
Counter,
|
|
12
|
+
{
|
|
13
|
+
readonly increment: (n: number) => Effect.Effect<number>;
|
|
14
|
+
}
|
|
15
|
+
>() {}
|
|
16
|
+
|
|
17
|
+
const requestIdRef = FiberRef.unsafeMake("missing");
|
|
18
|
+
|
|
19
|
+
const runtime = ManagedRuntime.make(
|
|
20
|
+
Layer.succeed(Counter, {
|
|
21
|
+
increment: (n: number) => Effect.succeed(n + 1),
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const contract = {
|
|
26
|
+
users: {
|
|
27
|
+
list: oc
|
|
28
|
+
.input(z.object({ amount: z.number() }))
|
|
29
|
+
.output(z.object({ next: z.number(), requestId: z.string() })),
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe("implementEffect", () => {
|
|
34
|
+
it("mirrors the contract tree and adds effect support on leaves", async () => {
|
|
35
|
+
const oe = implementEffect(contract, runtime);
|
|
36
|
+
|
|
37
|
+
expect(oe.users).toBeDefined();
|
|
38
|
+
expect(oe.users.list).toBeDefined();
|
|
39
|
+
expect(oe.users.list.handler).toBeTypeOf("function");
|
|
40
|
+
expect(oe.users.list.effect).toBeTypeOf("function");
|
|
41
|
+
|
|
42
|
+
const procedure = oe.users.list.effect(function* ({ input }) {
|
|
43
|
+
const counter = yield* Counter;
|
|
44
|
+
const requestId = yield* FiberRef.get(requestIdRef);
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
next: yield* counter.increment(input.amount),
|
|
48
|
+
requestId,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const result = await Effect.runPromise(
|
|
53
|
+
Effect.gen(function* () {
|
|
54
|
+
yield* FiberRef.set(requestIdRef, "req-123");
|
|
55
|
+
return yield* withFiberContext(() => call(procedure, { amount: 2 }));
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
expect(result).toEqual({
|
|
60
|
+
next: 3,
|
|
61
|
+
requestId: "req-123",
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("preserves contract enforcement at the root router", async () => {
|
|
66
|
+
const oe = implementEffect(contract, runtime);
|
|
67
|
+
|
|
68
|
+
const router = oe.router({
|
|
69
|
+
users: {
|
|
70
|
+
list: oe.users.list.effect(function* ({ input }) {
|
|
71
|
+
const counter = yield* Counter;
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
next: yield* counter.increment(input.amount),
|
|
75
|
+
requestId: yield* FiberRef.get(requestIdRef),
|
|
76
|
+
};
|
|
77
|
+
}),
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const result = await Effect.runPromise(
|
|
82
|
+
Effect.gen(function* () {
|
|
83
|
+
yield* FiberRef.set(requestIdRef, "req-456");
|
|
84
|
+
return yield* withFiberContext(() =>
|
|
85
|
+
call(router.users.list, { amount: 4 }),
|
|
86
|
+
);
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(result).toEqual({
|
|
91
|
+
next: 5,
|
|
92
|
+
requestId: "req-456",
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
expectTypeOf(router.users.list["~effect"]).toBeObject();
|
|
96
|
+
expectTypeOf(router).toExtend<
|
|
97
|
+
Router<typeof contract, Record<string, never>>
|
|
98
|
+
>();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("maps tagged errors and raw ORPCError values the same way as EffectBuilder", async () => {
|
|
102
|
+
class UserNotFoundError extends ORPCTaggedError("UserNotFoundError", {
|
|
103
|
+
code: "NOT_FOUND",
|
|
104
|
+
schema: z.object({ userId: z.string() }),
|
|
105
|
+
}) {}
|
|
106
|
+
|
|
107
|
+
const taggedContract = {
|
|
108
|
+
users: {
|
|
109
|
+
find: eoc
|
|
110
|
+
.errors({
|
|
111
|
+
NOT_FOUND: UserNotFoundError,
|
|
112
|
+
})
|
|
113
|
+
.input(z.object({ userId: z.string() }))
|
|
114
|
+
.output(z.object({ userId: z.string() })),
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const oe = implementEffect(taggedContract, runtime);
|
|
119
|
+
|
|
120
|
+
const taggedProcedure = oe.users.find.effect(function* ({ input }) {
|
|
121
|
+
return yield* Effect.fail(
|
|
122
|
+
new UserNotFoundError({ data: { userId: input.userId } }),
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
await expect(
|
|
127
|
+
call(taggedProcedure, { userId: "u-1" }),
|
|
128
|
+
).rejects.toMatchObject({
|
|
129
|
+
code: "NOT_FOUND",
|
|
130
|
+
data: { userId: "u-1" },
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const rawProcedure = oe.users.find.effect(function* () {
|
|
134
|
+
return yield* Effect.fail(
|
|
135
|
+
new ORPCError("FORBIDDEN", {
|
|
136
|
+
data: { userId: "nope" },
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await expect(call(rawProcedure, { userId: "u-2" })).rejects.toMatchObject({
|
|
142
|
+
code: "FORBIDDEN",
|
|
143
|
+
data: { userId: "nope" },
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("preserves tagged error constructors from eoc inside effect handlers", async () => {
|
|
148
|
+
class UserNotFoundError extends ORPCTaggedError("UserNotFoundError", {
|
|
149
|
+
schema: z.object({ userId: z.string() }),
|
|
150
|
+
}) {}
|
|
151
|
+
|
|
152
|
+
const taggedContract = {
|
|
153
|
+
users: {
|
|
154
|
+
find: eoc
|
|
155
|
+
.errors({
|
|
156
|
+
NOT_FOUND: UserNotFoundError,
|
|
157
|
+
FORBIDDEN: {},
|
|
158
|
+
})
|
|
159
|
+
.input(z.object({ userId: z.string() }))
|
|
160
|
+
.output(z.object({ userId: z.string() })),
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const oe = implementEffect(taggedContract, runtime);
|
|
165
|
+
let taggedError:
|
|
166
|
+
| ORPCError<"USER_NOT_FOUND_ERROR", { userId: string }>
|
|
167
|
+
| undefined;
|
|
168
|
+
let forbiddenError: ORPCError<"FORBIDDEN", unknown> | undefined;
|
|
169
|
+
|
|
170
|
+
const procedure = oe.users.find.effect(function* ({ input, errors }) {
|
|
171
|
+
taggedError = errors.NOT_FOUND({
|
|
172
|
+
data: { userId: input.userId },
|
|
173
|
+
});
|
|
174
|
+
forbiddenError = errors.FORBIDDEN();
|
|
175
|
+
|
|
176
|
+
return yield* Effect.fail(taggedError);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const result = call(procedure, { userId: "u-3" });
|
|
180
|
+
await expect(result).rejects.toMatchObject({
|
|
181
|
+
code: "USER_NOT_FOUND_ERROR",
|
|
182
|
+
data: { userId: "u-3" },
|
|
183
|
+
});
|
|
184
|
+
await expect(result).rejects.toBeInstanceOf(ORPCError);
|
|
185
|
+
|
|
186
|
+
expect(taggedError).toBeInstanceOf(UserNotFoundError);
|
|
187
|
+
expect(forbiddenError).toBeInstanceOf(ORPCError);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("preserves tagged error constructors with custom code from eoc inside effect handlers", async () => {
|
|
191
|
+
class UserNotFoundError extends ORPCTaggedError("UserNotFoundError", {
|
|
192
|
+
schema: z.object({ userId: z.string() }),
|
|
193
|
+
code: "NOT_FOUND",
|
|
194
|
+
}) {}
|
|
195
|
+
|
|
196
|
+
const taggedContract = {
|
|
197
|
+
users: {
|
|
198
|
+
find: eoc
|
|
199
|
+
.errors({
|
|
200
|
+
UserNotFoundError,
|
|
201
|
+
FORBIDDEN: {},
|
|
202
|
+
})
|
|
203
|
+
.input(z.object({ userId: z.string() }))
|
|
204
|
+
.output(z.object({ userId: z.string() })),
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const oe = implementEffect(taggedContract, runtime);
|
|
209
|
+
let taggedError: ORPCError<"NOT_FOUND", { userId: string }> | undefined;
|
|
210
|
+
let forbiddenError: ORPCError<"FORBIDDEN", unknown> | undefined;
|
|
211
|
+
|
|
212
|
+
const procedure = oe.users.find.effect(function* ({ input, errors }) {
|
|
213
|
+
taggedError = errors.UserNotFoundError({
|
|
214
|
+
data: { userId: input.userId },
|
|
215
|
+
});
|
|
216
|
+
forbiddenError = errors.FORBIDDEN();
|
|
217
|
+
|
|
218
|
+
return yield* Effect.fail(taggedError);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const result = call(procedure, { userId: "u-3" });
|
|
222
|
+
await expect(result).rejects.toMatchObject({
|
|
223
|
+
code: "NOT_FOUND",
|
|
224
|
+
data: { userId: "u-3" },
|
|
225
|
+
});
|
|
226
|
+
await expect(result).rejects.toBeInstanceOf(ORPCError);
|
|
227
|
+
|
|
228
|
+
expect(taggedError).toBeInstanceOf(UserNotFoundError);
|
|
229
|
+
expect(forbiddenError).toBeInstanceOf(ORPCError);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("preserves tagged error constructors through eoc.router contracts", async () => {
|
|
233
|
+
class UserNotFoundError extends ORPCTaggedError("UserNotFoundError", {
|
|
234
|
+
schema: z.object({ userId: z.string() }),
|
|
235
|
+
}) {}
|
|
236
|
+
|
|
237
|
+
const routedContract = eoc
|
|
238
|
+
.errors({
|
|
239
|
+
NOT_FOUND: UserNotFoundError,
|
|
240
|
+
})
|
|
241
|
+
.router({
|
|
242
|
+
users: {
|
|
243
|
+
find: oc
|
|
244
|
+
.input(z.object({ userId: z.string() }))
|
|
245
|
+
.output(z.object({ userId: z.string() })),
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const oe = implementEffect(routedContract, runtime);
|
|
250
|
+
let taggedError:
|
|
251
|
+
| ORPCError<"USER_NOT_FOUND_ERROR", { userId: string }>
|
|
252
|
+
| undefined;
|
|
253
|
+
|
|
254
|
+
const procedure = oe.users.find.effect(function* ({ input, errors }) {
|
|
255
|
+
taggedError = errors.NOT_FOUND({ data: { userId: input.userId } });
|
|
256
|
+
return yield* Effect.fail(taggedError);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const result = call(procedure, { userId: "u-4" });
|
|
260
|
+
await expect(result).rejects.toMatchObject({
|
|
261
|
+
code: "USER_NOT_FOUND_ERROR",
|
|
262
|
+
data: { userId: "u-4" },
|
|
263
|
+
});
|
|
264
|
+
await expect(result).rejects.toBeInstanceOf(ORPCError);
|
|
265
|
+
|
|
266
|
+
expect(taggedError).toBeInstanceOf(UserNotFoundError);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("preserves tagged error constructors with custom code through eoc.router contracts", async () => {
|
|
270
|
+
class UserNotFoundError extends ORPCTaggedError("UserNotFoundError", {
|
|
271
|
+
schema: z.object({ userId: z.string() }),
|
|
272
|
+
code: "NOT_FOUND",
|
|
273
|
+
}) {}
|
|
274
|
+
|
|
275
|
+
const routedContract = eoc
|
|
276
|
+
.errors({
|
|
277
|
+
UserNotFoundError,
|
|
278
|
+
})
|
|
279
|
+
.router({
|
|
280
|
+
users: {
|
|
281
|
+
find: oc
|
|
282
|
+
.input(z.object({ userId: z.string() }))
|
|
283
|
+
.output(z.object({ userId: z.string() })),
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const oe = implementEffect(routedContract, runtime);
|
|
288
|
+
let taggedError: ORPCError<"NOT_FOUND", { userId: string }> | undefined;
|
|
289
|
+
|
|
290
|
+
const procedure = oe.users.find.effect(function* ({ input, errors }) {
|
|
291
|
+
taggedError = errors.UserNotFoundError({
|
|
292
|
+
data: { userId: input.userId },
|
|
293
|
+
});
|
|
294
|
+
return yield* Effect.fail(taggedError);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const result = call(procedure, { userId: "u-4" });
|
|
298
|
+
await expect(result).rejects.toMatchObject({
|
|
299
|
+
code: "NOT_FOUND",
|
|
300
|
+
data: { userId: "u-4" },
|
|
301
|
+
});
|
|
302
|
+
await expect(result).rejects.toBeInstanceOf(ORPCError);
|
|
303
|
+
|
|
304
|
+
expect(taggedError).toBeInstanceOf(UserNotFoundError);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("retains contract-first restrictions at the type level", () => {
|
|
308
|
+
const oe = implementEffect(contract, runtime);
|
|
309
|
+
|
|
310
|
+
expectTypeOf(oe.users.list.handler).toBeFunction();
|
|
311
|
+
expectTypeOf(oe.users.list.effect).toBeFunction();
|
|
312
|
+
// @ts-expect-error input is not a property of the implementer
|
|
313
|
+
expect(oe.users.list.input).toBeUndefined();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("enforces contract output typing for handlers", () => {
|
|
317
|
+
const oe = implementEffect(contract, runtime);
|
|
318
|
+
|
|
319
|
+
oe.users.list.effect(
|
|
320
|
+
// @ts-expect-error effect() must return the contract output shape
|
|
321
|
+
function* () {
|
|
322
|
+
return { next: "wrong", requestId: 123 };
|
|
323
|
+
},
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
oe.users.list.handler(
|
|
327
|
+
// @ts-expect-error handler() must return the contract output shape
|
|
328
|
+
() => ({ next: "wrong", requestId: 123 }),
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
const procedure = oe.users.list.effect(function* ({ input }) {
|
|
332
|
+
return {
|
|
333
|
+
next: input.amount + 1,
|
|
334
|
+
requestId: "req-ok",
|
|
335
|
+
};
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
type ProcedureOutput = InferSchemaOutput<
|
|
339
|
+
NonNullable<(typeof procedure)["~orpc"]["outputSchema"]>
|
|
340
|
+
>;
|
|
341
|
+
expectTypeOf<ProcedureOutput>().toEqualTypeOf<{
|
|
342
|
+
next: number;
|
|
343
|
+
requestId: string;
|
|
344
|
+
}>();
|
|
345
|
+
});
|
|
346
|
+
});
|
|
@@ -50,8 +50,12 @@ describe("effectErrorMap types", () => {
|
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
it("should infer correct union type from EffectErrorMap", () => {
|
|
53
|
+
const zBadRequestData = z.object({
|
|
54
|
+
status: z.number(),
|
|
55
|
+
message: z.string(),
|
|
56
|
+
});
|
|
53
57
|
type TestErrorMap = {
|
|
54
|
-
BAD_REQUEST: {
|
|
58
|
+
BAD_REQUEST: { data: typeof zBadRequestData };
|
|
55
59
|
USER_NOT_FOUND_ERROR: typeof UserNotFoundError;
|
|
56
60
|
FORBIDDEN: typeof PermissionDenied;
|
|
57
61
|
};
|
|
@@ -59,8 +63,8 @@ describe("effectErrorMap types", () => {
|
|
|
59
63
|
type ErrorUnion = EffectErrorMapToUnion<TestErrorMap>;
|
|
60
64
|
|
|
61
65
|
// The union should include ORPCError for traditional and tagged error instances for classes
|
|
62
|
-
expectTypeOf<ErrorUnion>().
|
|
63
|
-
| ORPCError<"BAD_REQUEST",
|
|
66
|
+
expectTypeOf<ErrorUnion>().toExtend<
|
|
67
|
+
| ORPCError<"BAD_REQUEST", typeof zBadRequestData>
|
|
64
68
|
| ORPCTaggedErrorInstance<"UserNotFoundError", "USER_NOT_FOUND_ERROR">
|
|
65
69
|
| ORPCTaggedErrorInstance<"PermissionDenied", "FORBIDDEN">
|
|
66
70
|
>();
|
|
@@ -84,6 +88,21 @@ describe("isORPCTaggedErrorClass", () => {
|
|
|
84
88
|
});
|
|
85
89
|
|
|
86
90
|
describe("createEffectErrorConstructorMap", () => {
|
|
91
|
+
it("preserves declaration keys even when a tagged error class uses a different code", () => {
|
|
92
|
+
const errorMap = {
|
|
93
|
+
NOT_FOUND: UserNotFoundError,
|
|
94
|
+
} satisfies EffectErrorMap;
|
|
95
|
+
|
|
96
|
+
const constructorMap = createEffectErrorConstructorMap(errorMap);
|
|
97
|
+
const userNotFoundError = constructorMap.NOT_FOUND({
|
|
98
|
+
data: { userId: "123" },
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(constructorMap).not.toHaveProperty("USER_NOT_FOUND_ERROR");
|
|
102
|
+
expect(userNotFoundError).toBeInstanceOf(UserNotFoundError);
|
|
103
|
+
expect(userNotFoundError.code).toBe("USER_NOT_FOUND_ERROR");
|
|
104
|
+
});
|
|
105
|
+
|
|
87
106
|
it("should pass through tagged error classes", () => {
|
|
88
107
|
const errorMap = {
|
|
89
108
|
USER_NOT_FOUND_ERROR: UserNotFoundError,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Layer, ManagedRuntime } from "effect";
|
|
2
|
+
|
|
3
|
+
import { eoc } from "../index";
|
|
4
|
+
import {
|
|
5
|
+
baseErrorMap,
|
|
6
|
+
baseMeta,
|
|
7
|
+
inputSchema,
|
|
8
|
+
outputSchema,
|
|
9
|
+
pong,
|
|
10
|
+
} from "./shared";
|
|
11
|
+
|
|
12
|
+
export type InitialContext = { db: string };
|
|
13
|
+
export type CurrentContext = InitialContext & { auth: boolean };
|
|
14
|
+
|
|
15
|
+
export const runtime = ManagedRuntime.make(Layer.empty);
|
|
16
|
+
|
|
17
|
+
export const typedContract = {
|
|
18
|
+
ping: eoc
|
|
19
|
+
.errors(baseErrorMap)
|
|
20
|
+
.meta(baseMeta)
|
|
21
|
+
.input(inputSchema)
|
|
22
|
+
.output(outputSchema),
|
|
23
|
+
pong,
|
|
24
|
+
nested: {
|
|
25
|
+
ping: eoc
|
|
26
|
+
.errors(baseErrorMap)
|
|
27
|
+
.meta(baseMeta)
|
|
28
|
+
.input(inputSchema)
|
|
29
|
+
.output(outputSchema),
|
|
30
|
+
pong,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { OmitChainMethodDeep } from "@orpc/shared";
|
|
2
|
+
import { describe, expectTypeOf, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
EffectContractProcedureBuilderWithInputOutput,
|
|
6
|
+
EffectContractRouterBuilder,
|
|
7
|
+
} from "../index";
|
|
8
|
+
import { eoc } from "../index";
|
|
9
|
+
import {
|
|
10
|
+
type BaseMeta,
|
|
11
|
+
baseErrorMap,
|
|
12
|
+
generalSchema,
|
|
13
|
+
inputSchema,
|
|
14
|
+
outputSchema,
|
|
15
|
+
ping,
|
|
16
|
+
pong,
|
|
17
|
+
} from "./shared";
|
|
18
|
+
|
|
19
|
+
const generalBuilder = eoc
|
|
20
|
+
.$meta({ mode: "dev" } as BaseMeta)
|
|
21
|
+
.$input(inputSchema)
|
|
22
|
+
.errors(baseErrorMap);
|
|
23
|
+
|
|
24
|
+
describe("parity: @orpc/contract builder-variants.test-d.ts", () => {
|
|
25
|
+
describe("EffectContractProcedureBuilder", () => {
|
|
26
|
+
const builder = eoc.errors(baseErrorMap).meta({ mode: "dev" } as BaseMeta);
|
|
27
|
+
|
|
28
|
+
it("backward compatibility", () => {
|
|
29
|
+
const expected = {} as OmitChainMethodDeep<
|
|
30
|
+
typeof generalBuilder,
|
|
31
|
+
"$meta" | "$route" | "$input" | "prefix" | "tag" | "router"
|
|
32
|
+
>;
|
|
33
|
+
|
|
34
|
+
expectTypeOf<keyof typeof builder>().toEqualTypeOf<
|
|
35
|
+
keyof typeof expected
|
|
36
|
+
>();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it(".errors / .meta / .route / .input / .output", () => {
|
|
40
|
+
expectTypeOf(
|
|
41
|
+
builder.errors({ INVALID: { message: "invalid" } }),
|
|
42
|
+
).toBeObject();
|
|
43
|
+
expectTypeOf(builder.meta({ log: true })).toBeObject();
|
|
44
|
+
expectTypeOf(builder.route({ method: "GET" })).toBeObject();
|
|
45
|
+
expectTypeOf(builder.input(generalSchema)).toBeObject();
|
|
46
|
+
expectTypeOf(builder.output(generalSchema)).toBeObject();
|
|
47
|
+
|
|
48
|
+
// @ts-expect-error - schema is invalid
|
|
49
|
+
builder.errors({ TOO_MANY_REQUESTS: { data: {} } });
|
|
50
|
+
// @ts-expect-error - invalid method
|
|
51
|
+
builder.route({ method: "INVALID" });
|
|
52
|
+
// @ts-expect-error - schema is invalid
|
|
53
|
+
builder.input({});
|
|
54
|
+
// @ts-expect-error - schema is invalid
|
|
55
|
+
builder.output({});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("EffectContractProcedureBuilderWithInput", () => {
|
|
60
|
+
const builder = eoc
|
|
61
|
+
.errors(baseErrorMap)
|
|
62
|
+
.meta({ mode: "dev" })
|
|
63
|
+
.input(inputSchema);
|
|
64
|
+
|
|
65
|
+
it("backward compatibility", () => {
|
|
66
|
+
const expected = {} as OmitChainMethodDeep<
|
|
67
|
+
typeof generalBuilder,
|
|
68
|
+
"$meta" | "$route" | "$input" | "prefix" | "tag" | "router" | "input"
|
|
69
|
+
>;
|
|
70
|
+
|
|
71
|
+
expectTypeOf<keyof typeof builder>().toEqualTypeOf<
|
|
72
|
+
keyof typeof expected
|
|
73
|
+
>();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it(".errors / .meta / .route / .output", () => {
|
|
77
|
+
expectTypeOf(
|
|
78
|
+
builder.errors({ INVALID: { message: "invalid" } }),
|
|
79
|
+
).toBeObject();
|
|
80
|
+
expectTypeOf(builder.meta({ log: true })).toBeObject();
|
|
81
|
+
expectTypeOf(builder.route({ method: "GET" })).toBeObject();
|
|
82
|
+
expectTypeOf(builder.output(generalSchema)).toExtend<
|
|
83
|
+
EffectContractProcedureBuilderWithInputOutput<
|
|
84
|
+
typeof inputSchema,
|
|
85
|
+
typeof generalSchema,
|
|
86
|
+
typeof baseErrorMap,
|
|
87
|
+
BaseMeta
|
|
88
|
+
>
|
|
89
|
+
>();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("EffectContractProcedureBuilderWithOutput", () => {
|
|
94
|
+
const builder = eoc
|
|
95
|
+
.errors(baseErrorMap)
|
|
96
|
+
.meta({ mode: "dev" })
|
|
97
|
+
.output(outputSchema);
|
|
98
|
+
|
|
99
|
+
it("backward compatibility", () => {
|
|
100
|
+
const expected = {} as OmitChainMethodDeep<
|
|
101
|
+
typeof generalBuilder,
|
|
102
|
+
"$meta" | "$route" | "$input" | "prefix" | "tag" | "router" | "output"
|
|
103
|
+
>;
|
|
104
|
+
|
|
105
|
+
expectTypeOf<keyof typeof builder>().toEqualTypeOf<
|
|
106
|
+
keyof typeof expected
|
|
107
|
+
>();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it(".errors / .meta / .route / .input", () => {
|
|
111
|
+
expectTypeOf(
|
|
112
|
+
builder.errors({ INVALID: { message: "invalid" } }),
|
|
113
|
+
).toBeObject();
|
|
114
|
+
expectTypeOf(builder.meta({ log: true })).toBeObject();
|
|
115
|
+
expectTypeOf(builder.route({ method: "GET" })).toBeObject();
|
|
116
|
+
expectTypeOf(builder.input(generalSchema)).toExtend<
|
|
117
|
+
EffectContractProcedureBuilderWithInputOutput<
|
|
118
|
+
typeof generalSchema,
|
|
119
|
+
typeof outputSchema,
|
|
120
|
+
typeof baseErrorMap,
|
|
121
|
+
BaseMeta
|
|
122
|
+
>
|
|
123
|
+
>();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("EffectContractProcedureBuilderWithInputOutput", () => {
|
|
128
|
+
const builder = eoc
|
|
129
|
+
.errors(baseErrorMap)
|
|
130
|
+
.meta({ mode: "dev" })
|
|
131
|
+
.input(inputSchema)
|
|
132
|
+
.output(outputSchema);
|
|
133
|
+
|
|
134
|
+
it("backward compatibility", () => {
|
|
135
|
+
const expected = {} as OmitChainMethodDeep<
|
|
136
|
+
typeof generalBuilder,
|
|
137
|
+
| "$meta"
|
|
138
|
+
| "$route"
|
|
139
|
+
| "$input"
|
|
140
|
+
| "prefix"
|
|
141
|
+
| "tag"
|
|
142
|
+
| "router"
|
|
143
|
+
| "input"
|
|
144
|
+
| "output"
|
|
145
|
+
>;
|
|
146
|
+
|
|
147
|
+
expectTypeOf<keyof typeof builder>().toEqualTypeOf<
|
|
148
|
+
keyof typeof expected
|
|
149
|
+
>();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it(".errors / .meta / .route", () => {
|
|
153
|
+
expectTypeOf(
|
|
154
|
+
builder.errors({ INVALID: { message: "invalid" } }),
|
|
155
|
+
).toBeObject();
|
|
156
|
+
expectTypeOf(builder.meta({ log: true })).toBeObject();
|
|
157
|
+
expectTypeOf(builder.route({ method: "GET" })).toBeObject();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("EffectContractRouterBuilder", () => {
|
|
162
|
+
const builder = eoc.errors(baseErrorMap).prefix("/api");
|
|
163
|
+
|
|
164
|
+
it("backward compatibility", () => {
|
|
165
|
+
expectTypeOf(builder.errors).toBeFunction();
|
|
166
|
+
expectTypeOf(builder.prefix).toBeFunction();
|
|
167
|
+
expectTypeOf(builder.tag).toBeFunction();
|
|
168
|
+
expectTypeOf(builder.router).toBeFunction();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it(".errors / .prefix / .tag / .router", () => {
|
|
172
|
+
expectTypeOf(
|
|
173
|
+
builder.errors({ INVALID: { message: "invalid" } }),
|
|
174
|
+
).toBeObject();
|
|
175
|
+
expectTypeOf(builder.prefix("/api")).toExtend<
|
|
176
|
+
EffectContractRouterBuilder<typeof baseErrorMap, BaseMeta>
|
|
177
|
+
>();
|
|
178
|
+
expectTypeOf(builder.tag("tag1", "tag2")).toExtend<
|
|
179
|
+
EffectContractRouterBuilder<typeof baseErrorMap, BaseMeta>
|
|
180
|
+
>();
|
|
181
|
+
expectTypeOf(builder.router({ ping, pong })).toExtend<{
|
|
182
|
+
ping: typeof ping;
|
|
183
|
+
pong: typeof pong;
|
|
184
|
+
}>();
|
|
185
|
+
|
|
186
|
+
// @ts-expect-error - invalid prefix
|
|
187
|
+
builder.prefix(1);
|
|
188
|
+
// @ts-expect-error - invalid tag
|
|
189
|
+
builder.tag(1);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
});
|