effect-orpc 1.0.0-effect-v4.2 → 1.0.0-effect-v4.4

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.
@@ -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
+ });
@@ -0,0 +1,222 @@
1
+ import { ContractBuilder, isContractProcedure } from "@orpc/contract";
2
+ import { describe, expect, expectTypeOf, it } from "vitest";
3
+
4
+ import { effectErrorMapToErrorMap, eoc } from "../index";
5
+ import {
6
+ baseErrorMap,
7
+ baseMeta,
8
+ baseRoute,
9
+ generalSchema,
10
+ inputSchema,
11
+ outputSchema,
12
+ ping,
13
+ pong,
14
+ } from "./shared";
15
+
16
+ const builder = eoc
17
+ .$meta(baseMeta)
18
+ .$route(baseRoute)
19
+ .$input(inputSchema)
20
+ .errors(baseErrorMap);
21
+
22
+ describe("parity: @orpc/contract builder.test.ts", () => {
23
+ it("is a contract procedure", () => {
24
+ expect(eoc).toSatisfy(isContractProcedure);
25
+ });
26
+
27
+ it(".$meta", () => {
28
+ const meta = { dev: true, log: true };
29
+ const applied = builder.$meta(meta);
30
+
31
+ expect(applied).toBeInstanceOf(ContractBuilder);
32
+ expect(applied).not.toBe(builder);
33
+ expect(applied["~orpc"]).toEqual({
34
+ ...builder["~orpc"],
35
+ meta,
36
+ });
37
+ });
38
+
39
+ it(".$route", () => {
40
+ const route = { method: "GET", path: "/api" } as const;
41
+ const applied = builder.$route(route);
42
+
43
+ expect(applied).toBeInstanceOf(ContractBuilder);
44
+ expect(applied).not.toBe(builder);
45
+ expect(applied["~orpc"]).toEqual({
46
+ ...builder["~orpc"],
47
+ route,
48
+ });
49
+ });
50
+
51
+ it(".$input", () => {
52
+ const applied = builder.$input(generalSchema);
53
+
54
+ expect(applied).toBeInstanceOf(ContractBuilder);
55
+ expect(applied).not.toBe(builder);
56
+ expect(applied["~orpc"]).toEqual({
57
+ ...builder["~orpc"],
58
+ inputSchema: generalSchema,
59
+ });
60
+ });
61
+
62
+ it(".errors", () => {
63
+ const errors = {
64
+ BAD_GATEWAY: { data: outputSchema },
65
+ OVERRIDE: { message: "override" },
66
+ } as const;
67
+ const applied = builder.errors(errors);
68
+
69
+ expect(applied).toBeInstanceOf(ContractBuilder);
70
+ expect(applied).not.toBe(builder);
71
+ expect(applied["~orpc"]).toEqual({
72
+ ...builder["~orpc"],
73
+ errorMap: effectErrorMapToErrorMap({
74
+ ...baseErrorMap,
75
+ ...errors,
76
+ }),
77
+ });
78
+ });
79
+
80
+ it(".meta", () => {
81
+ const meta = { dev: true, log: true };
82
+ const applied = builder.meta(meta);
83
+
84
+ expect(applied).toBeInstanceOf(ContractBuilder);
85
+ expect(applied).not.toBe(builder);
86
+ expect(applied["~orpc"]).toEqual({
87
+ ...builder["~orpc"],
88
+ meta: { ...builder["~orpc"].meta, ...meta },
89
+ });
90
+ });
91
+
92
+ it(".route", () => {
93
+ const route = { method: "GET", path: "/path" } as const;
94
+ const applied = builder.route(route);
95
+
96
+ expect(applied).toBeInstanceOf(ContractBuilder);
97
+ expect(applied).not.toBe(builder);
98
+ expect(applied["~orpc"]).toEqual({
99
+ ...builder["~orpc"],
100
+ route: { ...builder["~orpc"].route, ...route },
101
+ });
102
+ });
103
+
104
+ it(".input", () => {
105
+ const applied = builder.input(generalSchema);
106
+
107
+ expect(applied).toBeInstanceOf(ContractBuilder);
108
+ expect(applied).not.toBe(builder);
109
+ expect(applied["~orpc"]).toEqual({
110
+ ...builder["~orpc"],
111
+ inputSchema: generalSchema,
112
+ });
113
+ });
114
+
115
+ it(".output", () => {
116
+ const applied = builder.output(generalSchema);
117
+
118
+ expect(applied).toBeInstanceOf(ContractBuilder);
119
+ expect(applied).not.toBe(builder);
120
+ expect(applied["~orpc"]).toEqual({
121
+ ...builder["~orpc"],
122
+ outputSchema: generalSchema,
123
+ });
124
+ });
125
+
126
+ it(".prefix", () => {
127
+ const applied = builder.prefix("/api") as any;
128
+
129
+ expect(applied).toBeInstanceOf(ContractBuilder);
130
+ expect(applied).not.toBe(builder);
131
+ expect(applied["~orpc"]).toEqual({
132
+ ...builder["~orpc"],
133
+ prefix: "/api",
134
+ });
135
+ });
136
+
137
+ it(".tag", () => {
138
+ const applied = builder.tag("tag1", "tag2") as any;
139
+
140
+ expect(applied).toBeInstanceOf(ContractBuilder);
141
+ expect(applied).not.toBe(builder);
142
+ expect(applied["~orpc"]).toEqual({
143
+ ...builder["~orpc"],
144
+ tags: ["tag1", "tag2"],
145
+ });
146
+ });
147
+
148
+ it(".router", () => {
149
+ const router = builder.router({ ping, pong });
150
+
151
+ expect(router.ping["~orpc"].route.path).toBe("/base");
152
+ expect(router.ping["~orpc"].meta).toEqual(baseMeta);
153
+ expect(router.ping["~orpc"].errorMap).toEqual(baseErrorMap);
154
+ });
155
+ });
156
+
157
+ describe("parity: @orpc/contract builder.test-d.ts", () => {
158
+ const typedBuilder = builder;
159
+
160
+ it("is a contract procedure", () => {
161
+ expectTypeOf(typedBuilder["~orpc"]).toBeObject();
162
+ });
163
+
164
+ it("preserves ContractBuilder typing for root methods", () => {
165
+ expectTypeOf(typedBuilder.$meta<{ auth?: boolean }>({})).toBeObject();
166
+
167
+ expectTypeOf(typedBuilder.$route({ method: "GET", path: "/api" })).toExtend<
168
+ typeof typedBuilder
169
+ >();
170
+
171
+ expectTypeOf(typedBuilder.$input(generalSchema)).toBeObject();
172
+
173
+ expectTypeOf(
174
+ typedBuilder.errors({
175
+ INVALID: { message: "invalid" },
176
+ OVERRIDE: { message: "override" },
177
+ }),
178
+ ).toBeObject();
179
+
180
+ expectTypeOf(typedBuilder.meta({ log: true })).toBeObject();
181
+ expectTypeOf(typedBuilder.route({ method: "GET" })).toBeObject();
182
+ expectTypeOf(typedBuilder.input(generalSchema)).toBeObject();
183
+ expectTypeOf(typedBuilder.output(generalSchema)).toBeObject();
184
+ expectTypeOf(typedBuilder.prefix("/api")).toBeObject();
185
+ expectTypeOf(typedBuilder.tag("tag1", "tag2")).toBeObject();
186
+ expectTypeOf(typedBuilder.router({ ping, pong })).toExtend<{
187
+ ping: typeof ping;
188
+ pong: typeof pong;
189
+ }>();
190
+ });
191
+
192
+ it("preserves ContractBuilder constraints", () => {
193
+ // @ts-expect-error - initial meta is required
194
+ typedBuilder.$meta<{ auth?: boolean }>();
195
+ // @ts-expect-error - auth is missing in initial meta
196
+ typedBuilder.$meta<{ auth: boolean }>({});
197
+ // @ts-expect-error - invalid method
198
+ typedBuilder.$route({ method: "INVALID" });
199
+ // @ts-expect-error - invalid schema
200
+ typedBuilder.$input({});
201
+ // @ts-expect-error - schema is invalid
202
+ typedBuilder.errors({ TOO_MANY_REQUESTS: { data: {} } });
203
+ // @ts-expect-error - invalid meta
204
+ typedBuilder.meta({ meta: "INVALID" });
205
+ // @ts-expect-error - invalid method
206
+ typedBuilder.route({ method: "INVALID" });
207
+ // @ts-expect-error - invalid schema
208
+ typedBuilder.input({});
209
+ // @ts-expect-error - invalid schema
210
+ typedBuilder.output({});
211
+ // @ts-expect-error - invalid prefix
212
+ typedBuilder.prefix(1);
213
+ // @ts-expect-error - invalid tag
214
+ typedBuilder.tag(1);
215
+ });
216
+
217
+ it("keeps eoc-specific tagged error normalization separate", () => {
218
+ const applied = eoc.errors(baseErrorMap);
219
+
220
+ expectTypeOf(applied["~orpc"].errorMap).toBeObject();
221
+ });
222
+ });
@@ -0,0 +1,193 @@
1
+ import type { ContractProcedure, Schema } from "@orpc/contract";
2
+ import type {
3
+ DecoratedMiddleware,
4
+ Middleware,
5
+ MiddlewareOutputFn,
6
+ } from "@orpc/server";
7
+ import type { OmitChainMethodDeep } from "@orpc/shared";
8
+ import { describe, expectTypeOf, it } from "vitest";
9
+ import z from "zod";
10
+
11
+ import {
12
+ EffectBuilder,
13
+ EffectDecoratedProcedure,
14
+ makeEffectORPC,
15
+ } from "../index";
16
+ import type { CurrentContext, InitialContext } from "./parity-shared";
17
+ import { runtime } from "./parity-shared";
18
+ import type { AssertExtends } from "./shared";
19
+ import {
20
+ baseErrorMap,
21
+ baseMeta,
22
+ type BaseMeta,
23
+ inputSchema,
24
+ outputSchema,
25
+ } from "./shared";
26
+
27
+ const typedBuilder = {} as EffectBuilder<
28
+ InitialContext,
29
+ CurrentContext,
30
+ typeof inputSchema,
31
+ typeof outputSchema,
32
+ typeof baseErrorMap,
33
+ BaseMeta,
34
+ never,
35
+ never
36
+ >;
37
+
38
+ const rootBuilder = makeEffectORPC(runtime)
39
+ .$context<InitialContext>()
40
+ .$meta(baseMeta)
41
+ .$input(inputSchema)
42
+ .errors(baseErrorMap);
43
+
44
+ const withMiddlewares = rootBuilder.use(({ next }) =>
45
+ next({ context: { auth: true as boolean } }),
46
+ );
47
+
48
+ const procedureBuilder = withMiddlewares.meta(baseMeta);
49
+ const withInput = procedureBuilder.input(inputSchema);
50
+ const withOutput = procedureBuilder.output(outputSchema);
51
+ const withInputOutput = withInput.output(outputSchema);
52
+
53
+ describe("parity: @orpc/server builder.test-d.ts", () => {
54
+ it("is a contract procedure", () => {
55
+ expectTypeOf<
56
+ AssertExtends<
57
+ typeof typedBuilder,
58
+ ContractProcedure<
59
+ typeof inputSchema,
60
+ typeof outputSchema,
61
+ typeof baseErrorMap,
62
+ BaseMeta
63
+ >
64
+ >
65
+ >().toEqualTypeOf<true>();
66
+ });
67
+
68
+ it(".$config / .$context / .$meta / .$route / .$input", () => {
69
+ expectTypeOf(
70
+ rootBuilder.$config({
71
+ initialInputValidationIndex: Number.NEGATIVE_INFINITY,
72
+ initialOutputValidationIndex: Number.POSITIVE_INFINITY,
73
+ }),
74
+ ).toBeObject();
75
+
76
+ expectTypeOf(rootBuilder.$context()).toBeObject();
77
+ expectTypeOf(rootBuilder.$context<{ anything: string }>()).toBeObject();
78
+ expectTypeOf(rootBuilder.$meta<{ auth?: boolean }>({})).toBeObject();
79
+ expectTypeOf(rootBuilder.$route({ method: "GET" })).toBeObject();
80
+
81
+ const schema = z.void();
82
+ expectTypeOf(rootBuilder.$input(schema)).toBeObject();
83
+ expectTypeOf(rootBuilder.$input<Schema<void, unknown>>()).toBeObject();
84
+
85
+ rootBuilder.$config({
86
+ // @ts-expect-error - must be number
87
+ initialInputValidationIndex: "INVALID",
88
+ });
89
+ // @ts-expect-error - initial meta is required
90
+ rootBuilder.$meta<{ auth?: boolean }>();
91
+ // @ts-expect-error - invalid method
92
+ rootBuilder.$route({ method: "INVALID" });
93
+ // @ts-expect-error - invalid schema
94
+ rootBuilder.$input<"invalid">();
95
+ });
96
+
97
+ it(".middleware", () => {
98
+ expectTypeOf(
99
+ rootBuilder.middleware(
100
+ ({ next }, input: "input", output: MiddlewareOutputFn<"output">) => {
101
+ expectTypeOf(input).toEqualTypeOf<"input">();
102
+ expectTypeOf(output).toEqualTypeOf<MiddlewareOutputFn<"output">>();
103
+ return next({ context: { extra: true } });
104
+ },
105
+ ),
106
+ ).toExtend<
107
+ DecoratedMiddleware<
108
+ InitialContext,
109
+ { extra: boolean },
110
+ "input",
111
+ "output",
112
+ any,
113
+ BaseMeta
114
+ >
115
+ >();
116
+ });
117
+
118
+ it(".errors / .use / .meta / .route / .input / .output", () => {
119
+ expectTypeOf(
120
+ rootBuilder.errors({ BAD_GATEWAY: { message: "BAD" } }),
121
+ ).toBeObject();
122
+ expectTypeOf(withMiddlewares.use).toBeFunction();
123
+ expectTypeOf(withMiddlewares.meta).toBeFunction();
124
+ expectTypeOf(withMiddlewares.route).toBeFunction();
125
+ expectTypeOf(withMiddlewares.input).toBeFunction();
126
+ expectTypeOf(withMiddlewares.output).toBeFunction();
127
+
128
+ const mid = {} as Middleware<
129
+ { cacheable?: boolean } & Record<never, never>,
130
+ Record<never, never>,
131
+ unknown,
132
+ unknown,
133
+ any,
134
+ BaseMeta
135
+ >;
136
+ expectTypeOf(withMiddlewares.use(mid)).toBeObject();
137
+ });
138
+
139
+ it(".handler / .effect / .prefix / .tag / .router / .lazy", () => {
140
+ expectTypeOf(withOutput.handler(() => ({ output: 456 }))).toExtend<
141
+ EffectDecoratedProcedure<
142
+ InitialContext & Record<never, never>,
143
+ CurrentContext,
144
+ typeof inputSchema,
145
+ typeof outputSchema,
146
+ typeof baseErrorMap,
147
+ BaseMeta,
148
+ never,
149
+ never
150
+ >
151
+ >();
152
+
153
+ expectTypeOf(
154
+ withOutput.effect(function* () {
155
+ return { output: 456 };
156
+ }),
157
+ ).toExtend<
158
+ EffectDecoratedProcedure<
159
+ InitialContext & Record<never, never>,
160
+ CurrentContext,
161
+ typeof inputSchema,
162
+ typeof outputSchema,
163
+ typeof baseErrorMap,
164
+ BaseMeta,
165
+ never,
166
+ never
167
+ >
168
+ >();
169
+
170
+ expectTypeOf(rootBuilder.prefix("/api")).toBeObject();
171
+ expectTypeOf(rootBuilder.tag("tag1", "tag2")).toBeObject();
172
+ });
173
+ });
174
+
175
+ describe("parity: @orpc/server builder-variants.test-d.ts", () => {
176
+ it("keeps variant method surface aligned with upstream", () => {
177
+ void ({} as OmitChainMethodDeep<
178
+ typeof typedBuilder,
179
+ "$config" | "$context" | "$meta" | "$route" | "$input" | "middleware"
180
+ >);
181
+
182
+ expectTypeOf(withMiddlewares.errors).toBeFunction();
183
+ expectTypeOf(withMiddlewares.use).toBeFunction();
184
+ expectTypeOf(withMiddlewares.handler).toBeFunction();
185
+ expectTypeOf(withMiddlewares.effect).toBeFunction();
186
+
187
+ expectTypeOf(withMiddlewares.handler).toBeFunction();
188
+ expectTypeOf(procedureBuilder.traced).toBeFunction();
189
+ expectTypeOf(withInput.output).toBeFunction();
190
+ expectTypeOf(withOutput.input).toBeFunction();
191
+ expectTypeOf(withInputOutput.effect).toBeFunction();
192
+ });
193
+ });