@shivaedev/effect-trpc 0.1.1 → 0.3.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/CHANGELOG.md +16 -0
- package/README.md +24 -7
- package/dist/adapter.d.ts +15 -3
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/context-bridge.d.ts +4 -1
- package/dist/internal/context-bridge.d.ts.map +1 -1
- package/dist/internal/context-bridge.js +8 -1
- package/dist/internal/context-bridge.js.map +1 -1
- package/dist/internal/procedure-types.d.ts +2 -1
- package/dist/internal/procedure-types.d.ts.map +1 -1
- package/dist/internal/runtime.d.ts +5 -2
- package/dist/internal/runtime.d.ts.map +1 -1
- package/dist/internal/runtime.js +48 -16
- package/dist/internal/runtime.js.map +1 -1
- package/dist/internal/subscription-handler.d.ts +14 -0
- package/dist/internal/subscription-handler.d.ts.map +1 -0
- package/dist/internal/subscription-handler.js +18 -0
- package/dist/internal/subscription-handler.js.map +1 -0
- package/dist/internal/vitest-trpc.d.ts +2 -1
- package/dist/internal/vitest-trpc.d.ts.map +1 -1
- package/dist/internal/vitest-trpc.js +8 -3
- package/dist/internal/vitest-trpc.js.map +1 -1
- package/dist/procedure.d.ts +10 -3
- package/dist/procedure.d.ts.map +1 -1
- package/dist/procedure.js +16 -3
- package/dist/procedure.js.map +1 -1
- package/dist/request-signal.d.ts +4 -0
- package/dist/request-signal.d.ts.map +1 -0
- package/dist/request-signal.js +4 -0
- package/dist/request-signal.js.map +1 -0
- package/dist/testing/types.d.ts +19 -9
- package/dist/testing/types.d.ts.map +1 -1
- package/dist/testing/vitest.d.ts +2 -1
- package/dist/testing/vitest.d.ts.map +1 -1
- package/dist/testing/vitest.js +29 -22
- package/dist/testing/vitest.js.map +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/types.d.ts +3 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapter.ts +29 -4
- package/src/index.ts +3 -0
- package/src/internal/context-bridge.ts +14 -1
- package/src/internal/procedure-types.ts +9 -1
- package/src/internal/runtime.ts +72 -38
- package/src/internal/subscription-handler.ts +61 -0
- package/src/internal/vitest-trpc.ts +34 -17
- package/src/procedure.ts +58 -0
- package/src/request-signal.ts +7 -0
- package/src/testing/types.ts +61 -14
- package/src/testing/vitest.ts +72 -26
- package/src/testing.ts +7 -1
- package/src/types.ts +7 -2
|
@@ -4,7 +4,9 @@ import * as TestClock from "effect/testing/TestClock";
|
|
|
4
4
|
import * as TestConsole from "effect/testing/TestConsole";
|
|
5
5
|
import { makeEffectCallerFactory } from "../testing/caller.js";
|
|
6
6
|
import type {
|
|
7
|
-
|
|
7
|
+
TrpcHarnessTest,
|
|
8
|
+
TrpcHarnessTester,
|
|
9
|
+
TrpcHarnessTestRuntimeOptions,
|
|
8
10
|
TrpcTester,
|
|
9
11
|
TrpcTestRuntimeOptions,
|
|
10
12
|
} from "../testing/types.js";
|
|
@@ -69,22 +71,24 @@ const runEffectTest = <A, E>(
|
|
|
69
71
|
{ signal: context.signal },
|
|
70
72
|
);
|
|
71
73
|
|
|
72
|
-
export const
|
|
74
|
+
export const makeTrpcHarnessTester = <
|
|
73
75
|
Options,
|
|
74
76
|
Caller extends object,
|
|
77
|
+
Harness,
|
|
75
78
|
Provided,
|
|
76
79
|
LayerError,
|
|
77
80
|
>(
|
|
78
81
|
fixtureIt: FixtureTestApi<Provided>,
|
|
79
|
-
options:
|
|
80
|
-
|
|
82
|
+
options: TrpcHarnessTestRuntimeOptions<
|
|
83
|
+
Options,
|
|
84
|
+
Caller,
|
|
85
|
+
Harness,
|
|
86
|
+
Provided,
|
|
87
|
+
LayerError
|
|
88
|
+
>,
|
|
89
|
+
): TrpcHarnessTester<Harness, Provided> => {
|
|
81
90
|
const run = <A, Eff extends Effect.Effect<unknown, unknown, Provided>>(
|
|
82
|
-
body: (
|
|
83
|
-
trpc: ReturnType<
|
|
84
|
-
typeof makeEffectCallerFactory<Options, Caller, Provided>
|
|
85
|
-
>,
|
|
86
|
-
context: TestContext,
|
|
87
|
-
) => Generator<Eff, A, never>,
|
|
91
|
+
body: (harness: Harness, context: TestContext) => Generator<Eff, A, never>,
|
|
88
92
|
context: TestContext,
|
|
89
93
|
): Effect.Effect<A, unknown, Scope.Scope> => {
|
|
90
94
|
const program = Effect.gen(function* () {
|
|
@@ -94,7 +98,8 @@ export const makeTrpcTester = <
|
|
|
94
98
|
options.createCaller,
|
|
95
99
|
services,
|
|
96
100
|
);
|
|
97
|
-
|
|
101
|
+
const harness = yield* options.makeHarness(trpc, context);
|
|
102
|
+
return yield* Effect.gen(() => body(harness, context));
|
|
98
103
|
});
|
|
99
104
|
// The body constraint proves every yielded service is part of Provided.
|
|
100
105
|
// TypeScript cannot reduce that generic generator requirement here.
|
|
@@ -106,7 +111,7 @@ export const makeTrpcTester = <
|
|
|
106
111
|
};
|
|
107
112
|
|
|
108
113
|
const register =
|
|
109
|
-
(current: FixtureTestApi<Provided>):
|
|
114
|
+
(current: FixtureTestApi<Provided>): TrpcHarnessTest<Harness, Provided> =>
|
|
110
115
|
(name, body, testOptions) =>
|
|
111
116
|
current(
|
|
112
117
|
name,
|
|
@@ -138,7 +143,7 @@ export const makeTrpcTester = <
|
|
|
138
143
|
|
|
139
144
|
const make = (
|
|
140
145
|
current: FixtureTestApi<Provided>,
|
|
141
|
-
):
|
|
146
|
+
): TrpcHarnessTester<Harness, Provided> => {
|
|
142
147
|
const test = register(current);
|
|
143
148
|
return Object.assign(test, {
|
|
144
149
|
skip: register(current.skip),
|
|
@@ -151,9 +156,7 @@ export const makeTrpcTester = <
|
|
|
151
156
|
name: string,
|
|
152
157
|
body: (
|
|
153
158
|
item: Item,
|
|
154
|
-
|
|
155
|
-
typeof makeEffectCallerFactory<Options, Caller, Provided>
|
|
156
|
-
>,
|
|
159
|
+
harness: Harness,
|
|
157
160
|
context: TestContext,
|
|
158
161
|
) => Generator<Eff, A, never>,
|
|
159
162
|
testOptions?: number | TestOptions,
|
|
@@ -187,7 +190,7 @@ export const makeTrpcTester = <
|
|
|
187
190
|
});
|
|
188
191
|
return runEffectTest(
|
|
189
192
|
run(
|
|
190
|
-
(
|
|
193
|
+
(harness, testContext) => body(item, harness, testContext),
|
|
191
194
|
context,
|
|
192
195
|
),
|
|
193
196
|
context,
|
|
@@ -200,3 +203,17 @@ export const makeTrpcTester = <
|
|
|
200
203
|
|
|
201
204
|
return make(fixtureIt);
|
|
202
205
|
};
|
|
206
|
+
|
|
207
|
+
export const makeTrpcTester = <
|
|
208
|
+
Options,
|
|
209
|
+
Caller extends object,
|
|
210
|
+
Provided,
|
|
211
|
+
LayerError,
|
|
212
|
+
>(
|
|
213
|
+
fixtureIt: FixtureTestApi<Provided>,
|
|
214
|
+
options: TrpcTestRuntimeOptions<Options, Caller, Provided, LayerError>,
|
|
215
|
+
): TrpcTester<Options, Caller, Provided> =>
|
|
216
|
+
makeTrpcHarnessTester(fixtureIt, {
|
|
217
|
+
...options,
|
|
218
|
+
makeHarness: (trpc) => Effect.succeed(trpc),
|
|
219
|
+
}) as TrpcTester<Options, Caller, Provided>;
|
package/src/procedure.ts
CHANGED
|
@@ -2,17 +2,20 @@ import type {
|
|
|
2
2
|
TRPCMutationProcedure,
|
|
3
3
|
TRPCProcedureBuilder,
|
|
4
4
|
TRPCQueryProcedure,
|
|
5
|
+
TRPCSubscriptionProcedure,
|
|
5
6
|
} from "@trpc/server";
|
|
6
7
|
import { Schema } from "effect";
|
|
7
8
|
import { makeProcedureHandler } from "./internal/procedure-handler.js";
|
|
8
9
|
import type {
|
|
9
10
|
DefaultValue,
|
|
10
11
|
EffectProcedureResolver,
|
|
12
|
+
EffectSubscriptionResolver,
|
|
11
13
|
IntersectIfDefined,
|
|
12
14
|
ResolverContext,
|
|
13
15
|
} from "./internal/procedure-types.js";
|
|
14
16
|
import type { RuntimeBridge } from "./internal/runtime.js";
|
|
15
17
|
import { captureStackTrace } from "./internal/stack-trace.js";
|
|
18
|
+
import { makeSubscriptionHandler } from "./internal/subscription-handler.js";
|
|
16
19
|
import type { EffectProcedureRequestServices } from "./request-services.js";
|
|
17
20
|
|
|
18
21
|
type Builder<
|
|
@@ -72,6 +75,8 @@ export class EffectProcedureBuilder<
|
|
|
72
75
|
LayerError
|
|
73
76
|
>,
|
|
74
77
|
private readonly runtime: RuntimeBridge<RuntimeRequirements>,
|
|
78
|
+
private readonly subscriptionBuilder = builder,
|
|
79
|
+
private readonly subscriptionOutput?: Schema.ConstraintDecoder<unknown>,
|
|
75
80
|
) {}
|
|
76
81
|
|
|
77
82
|
input<SchemaValue extends Schema.ConstraintDecoder<unknown>>(
|
|
@@ -89,6 +94,9 @@ export class EffectProcedureBuilder<
|
|
|
89
94
|
RuntimeRequirements
|
|
90
95
|
> {
|
|
91
96
|
const next = this.builder.input(Schema.toStandardSchemaV1(schema) as never);
|
|
97
|
+
const subscriptionNext = this.subscriptionBuilder.input(
|
|
98
|
+
Schema.toStandardSchemaV1(schema) as never,
|
|
99
|
+
);
|
|
92
100
|
return new EffectProcedureBuilder(
|
|
93
101
|
next as unknown as Builder<
|
|
94
102
|
Context,
|
|
@@ -101,6 +109,16 @@ export class EffectProcedureBuilder<
|
|
|
101
109
|
>,
|
|
102
110
|
this.requestServices,
|
|
103
111
|
this.runtime,
|
|
112
|
+
subscriptionNext as unknown as Builder<
|
|
113
|
+
Context,
|
|
114
|
+
Meta,
|
|
115
|
+
ContextOverrides,
|
|
116
|
+
IntersectIfDefined<InputIn, SchemaValue["Encoded"]>,
|
|
117
|
+
IntersectIfDefined<InputOut, SchemaValue["Type"]>,
|
|
118
|
+
OutputIn,
|
|
119
|
+
OutputOut
|
|
120
|
+
>,
|
|
121
|
+
this.subscriptionOutput,
|
|
104
122
|
);
|
|
105
123
|
}
|
|
106
124
|
|
|
@@ -131,6 +149,16 @@ export class EffectProcedureBuilder<
|
|
|
131
149
|
>,
|
|
132
150
|
this.requestServices,
|
|
133
151
|
this.runtime,
|
|
152
|
+
this.subscriptionBuilder as unknown as Builder<
|
|
153
|
+
Context,
|
|
154
|
+
Meta,
|
|
155
|
+
ContextOverrides,
|
|
156
|
+
InputIn,
|
|
157
|
+
InputOut,
|
|
158
|
+
IntersectIfDefined<OutputIn, SchemaValue["Encoded"]>,
|
|
159
|
+
IntersectIfDefined<OutputOut, SchemaValue["Type"]>
|
|
160
|
+
>,
|
|
161
|
+
schema,
|
|
134
162
|
);
|
|
135
163
|
}
|
|
136
164
|
|
|
@@ -170,6 +198,36 @@ export class EffectProcedureBuilder<
|
|
|
170
198
|
}>;
|
|
171
199
|
}
|
|
172
200
|
|
|
201
|
+
subscription<Output>(
|
|
202
|
+
resolver: EffectSubscriptionResolver<
|
|
203
|
+
DefaultValue<InputOut, undefined>,
|
|
204
|
+
ProvidedServices | NoInfer<RuntimeRequirements>,
|
|
205
|
+
DefaultValue<OutputIn, Output>
|
|
206
|
+
>,
|
|
207
|
+
): TRPCSubscriptionProcedure<{
|
|
208
|
+
input: DefaultValue<InputIn, void>;
|
|
209
|
+
output: AsyncIterable<DefaultValue<OutputOut, Output>, void, unknown>;
|
|
210
|
+
meta: Meta;
|
|
211
|
+
}> {
|
|
212
|
+
const handler = makeSubscriptionHandler(
|
|
213
|
+
this.runtime,
|
|
214
|
+
resolver,
|
|
215
|
+
this.requestServices,
|
|
216
|
+
{
|
|
217
|
+
captureStackTrace: captureStackTrace(),
|
|
218
|
+
type: "subscription",
|
|
219
|
+
},
|
|
220
|
+
this.subscriptionOutput,
|
|
221
|
+
) as never;
|
|
222
|
+
return this.subscriptionBuilder.subscription(
|
|
223
|
+
handler,
|
|
224
|
+
) as TRPCSubscriptionProcedure<{
|
|
225
|
+
input: DefaultValue<InputIn, void>;
|
|
226
|
+
output: AsyncIterable<DefaultValue<OutputOut, Output>, void, unknown>;
|
|
227
|
+
meta: Meta;
|
|
228
|
+
}>;
|
|
229
|
+
}
|
|
230
|
+
|
|
173
231
|
private dispatch<Output>(
|
|
174
232
|
type: "mutation" | "query",
|
|
175
233
|
resolver: EffectProcedureResolver<
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
|
|
3
|
+
/** The transport cancellation signal for the current tRPC procedure. */
|
|
4
|
+
export const RequestSignal = Context.Reference<AbortSignal | undefined>(
|
|
5
|
+
"@shivaedev/effect-trpc/RequestSignal",
|
|
6
|
+
{ defaultValue: () => undefined },
|
|
7
|
+
);
|
package/src/testing/types.ts
CHANGED
|
@@ -3,45 +3,57 @@ import type { Effect, Layer } from "effect";
|
|
|
3
3
|
import type { EffectTRPCAdapter } from "../adapter.js";
|
|
4
4
|
import type { EffectCallerFactory } from "./caller.js";
|
|
5
5
|
|
|
6
|
-
export type
|
|
6
|
+
export type TrpcHarnessTest<Harness, Provided> = <
|
|
7
7
|
A,
|
|
8
8
|
Eff extends Effect.Effect<unknown, unknown, Provided>,
|
|
9
9
|
>(
|
|
10
10
|
name: string,
|
|
11
|
-
body: (
|
|
12
|
-
trpc: EffectCallerFactory<Options, Caller>,
|
|
13
|
-
context: TestContext,
|
|
14
|
-
) => Generator<Eff, A, never>,
|
|
11
|
+
body: (harness: Harness, context: TestContext) => Generator<Eff, A, never>,
|
|
15
12
|
options?: number | TestOptions,
|
|
16
13
|
) => void;
|
|
17
14
|
|
|
18
|
-
export interface
|
|
19
|
-
extends
|
|
20
|
-
readonly skip:
|
|
21
|
-
readonly skipIf: (condition: unknown) =>
|
|
22
|
-
readonly runIf: (condition: unknown) =>
|
|
23
|
-
readonly only:
|
|
15
|
+
export interface TrpcHarnessTester<Harness, Provided>
|
|
16
|
+
extends TrpcHarnessTest<Harness, Provided> {
|
|
17
|
+
readonly skip: TrpcHarnessTest<Harness, Provided>;
|
|
18
|
+
readonly skipIf: (condition: unknown) => TrpcHarnessTest<Harness, Provided>;
|
|
19
|
+
readonly runIf: (condition: unknown) => TrpcHarnessTest<Harness, Provided>;
|
|
20
|
+
readonly only: TrpcHarnessTest<Harness, Provided>;
|
|
24
21
|
readonly each: <Item>(
|
|
25
22
|
cases: ReadonlyArray<Item>,
|
|
26
23
|
) => <A, Eff extends Effect.Effect<unknown, unknown, Provided>>(
|
|
27
24
|
name: string,
|
|
28
25
|
body: (
|
|
29
26
|
item: Item,
|
|
30
|
-
|
|
27
|
+
harness: Harness,
|
|
31
28
|
context: TestContext,
|
|
32
29
|
) => Generator<Eff, A, never>,
|
|
33
30
|
options?: number | TestOptions,
|
|
34
31
|
) => void;
|
|
35
|
-
readonly fails:
|
|
32
|
+
readonly fails: TrpcHarnessTest<Harness, Provided>;
|
|
36
33
|
}
|
|
37
34
|
|
|
35
|
+
export type TrpcTest<Options, Caller, Provided> = TrpcHarnessTest<
|
|
36
|
+
EffectCallerFactory<Options, Caller>,
|
|
37
|
+
Provided
|
|
38
|
+
>;
|
|
39
|
+
|
|
40
|
+
export type TrpcTester<Options, Caller, Provided> = TrpcHarnessTester<
|
|
41
|
+
EffectCallerFactory<Options, Caller>,
|
|
42
|
+
Provided
|
|
43
|
+
>;
|
|
44
|
+
|
|
38
45
|
export type TrpcIt<Options, Caller, Provided> = Vitest.Methods & {
|
|
39
46
|
readonly effectTRPC: TrpcTester<Options, Caller, Provided>;
|
|
40
47
|
};
|
|
41
48
|
|
|
42
|
-
export
|
|
49
|
+
export type TrpcHarnessIt<Harness, Provided> = Vitest.Methods & {
|
|
50
|
+
readonly effectTRPC: TrpcHarnessTester<Harness, Provided>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export interface TrpcHarnessTestRuntimeOptions<
|
|
43
54
|
Options,
|
|
44
55
|
Caller extends object,
|
|
56
|
+
Harness,
|
|
45
57
|
Provided,
|
|
46
58
|
LayerError,
|
|
47
59
|
> {
|
|
@@ -51,8 +63,28 @@ export interface TrpcTestRuntimeOptions<
|
|
|
51
63
|
) => Effect.Effect<A, unknown, Provided>;
|
|
52
64
|
readonly createCaller: (options?: Options) => Caller;
|
|
53
65
|
readonly layer: Layer.Layer<Provided, LayerError>;
|
|
66
|
+
readonly makeHarness: (
|
|
67
|
+
trpc: EffectCallerFactory<Options, Caller>,
|
|
68
|
+
context: TestContext,
|
|
69
|
+
) => Effect.Effect<Harness, unknown, Provided>;
|
|
54
70
|
}
|
|
55
71
|
|
|
72
|
+
export type TrpcTestRuntimeOptions<
|
|
73
|
+
Options,
|
|
74
|
+
Caller extends object,
|
|
75
|
+
Provided,
|
|
76
|
+
LayerError,
|
|
77
|
+
> = Omit<
|
|
78
|
+
TrpcHarnessTestRuntimeOptions<
|
|
79
|
+
Options,
|
|
80
|
+
Caller,
|
|
81
|
+
EffectCallerFactory<Options, Caller>,
|
|
82
|
+
Provided,
|
|
83
|
+
LayerError
|
|
84
|
+
>,
|
|
85
|
+
"makeHarness"
|
|
86
|
+
>;
|
|
87
|
+
|
|
56
88
|
export interface MakeTrpcItOptions<
|
|
57
89
|
CreateCaller extends (...arguments_: never[]) => object,
|
|
58
90
|
// biome-ignore lint/suspicious/noExplicitAny: Layer output and error are recovered with Layer utility types
|
|
@@ -66,6 +98,21 @@ export interface MakeTrpcItOptions<
|
|
|
66
98
|
readonly layer: TestLayer;
|
|
67
99
|
}
|
|
68
100
|
|
|
101
|
+
export interface MakeTrpcHarnessItOptions<
|
|
102
|
+
CreateCaller extends (...arguments_: never[]) => object,
|
|
103
|
+
// biome-ignore lint/suspicious/noExplicitAny: Layer output and error are recovered with Layer utility types
|
|
104
|
+
TestLayer extends Layer.Layer<any, any, never>,
|
|
105
|
+
Harness,
|
|
106
|
+
> extends MakeTrpcItOptions<CreateCaller, TestLayer> {
|
|
107
|
+
readonly makeHarness: (
|
|
108
|
+
trpc: EffectCallerFactory<
|
|
109
|
+
CallerOptions<CreateCaller>,
|
|
110
|
+
CallerResult<CreateCaller>
|
|
111
|
+
>,
|
|
112
|
+
context: TestContext,
|
|
113
|
+
) => Effect.Effect<Harness, unknown, Layer.Success<TestLayer>>;
|
|
114
|
+
}
|
|
115
|
+
|
|
69
116
|
export type CallerOptions<CreateCaller> = CreateCaller extends (
|
|
70
117
|
...arguments_: infer Arguments
|
|
71
118
|
) => object
|
package/src/testing/vitest.ts
CHANGED
|
@@ -1,17 +1,54 @@
|
|
|
1
|
-
import { it as effectIt } from "@effect/vitest";
|
|
1
|
+
import { it as effectIt, type Vitest } from "@effect/vitest";
|
|
2
2
|
import { Effect, Exit, Layer, Scope } from "effect";
|
|
3
3
|
import {
|
|
4
4
|
type FixtureTestApi,
|
|
5
5
|
fixtureName,
|
|
6
|
+
makeTrpcHarnessTester,
|
|
6
7
|
makeTrpcTester,
|
|
7
8
|
} from "../internal/vitest-trpc.js";
|
|
8
9
|
import type {
|
|
9
10
|
CallerOptions,
|
|
10
11
|
CallerResult,
|
|
12
|
+
MakeTrpcHarnessItOptions,
|
|
11
13
|
MakeTrpcItOptions,
|
|
14
|
+
TrpcHarnessIt,
|
|
12
15
|
TrpcIt,
|
|
13
16
|
} from "./types.js";
|
|
14
17
|
|
|
18
|
+
const makeFixtureIt = <Provided, LayerError>(
|
|
19
|
+
layer: Layer.Layer<Provided, LayerError>,
|
|
20
|
+
): FixtureTestApi<Provided> =>
|
|
21
|
+
effectIt.extend(
|
|
22
|
+
fixtureName,
|
|
23
|
+
{ scope: "worker" },
|
|
24
|
+
// biome-ignore lint/correctness/noEmptyPattern: Vitest fixtures require a destructured context parameter.
|
|
25
|
+
async ({}, { onCleanup }) => {
|
|
26
|
+
const scope = Effect.runSync(Scope.make());
|
|
27
|
+
onCleanup(() => Effect.runPromise(Scope.close(scope, Exit.void)));
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
return await Effect.runPromise(Layer.buildWithScope(layer, scope));
|
|
31
|
+
} catch (error) {
|
|
32
|
+
await Effect.runPromise(Scope.close(scope, Exit.void));
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
) as unknown as FixtureTestApi<Provided>;
|
|
37
|
+
|
|
38
|
+
const withEffectTRPC = <Tester>(
|
|
39
|
+
tester: Tester,
|
|
40
|
+
): Vitest.Methods & {
|
|
41
|
+
readonly effectTRPC: Tester;
|
|
42
|
+
} =>
|
|
43
|
+
new Proxy(effectIt, {
|
|
44
|
+
get(target, property, receiver) {
|
|
45
|
+
if (property === "effectTRPC") {
|
|
46
|
+
return tester;
|
|
47
|
+
}
|
|
48
|
+
return Reflect.get(target, property, receiver);
|
|
49
|
+
},
|
|
50
|
+
}) as Vitest.Methods & { readonly effectTRPC: Tester };
|
|
51
|
+
|
|
15
52
|
export const makeTrpcIt = <
|
|
16
53
|
CreateCaller extends (...arguments_: never[]) => object,
|
|
17
54
|
// biome-ignore lint/suspicious/noExplicitAny: Layer output and error are recovered with Layer utility types
|
|
@@ -27,23 +64,8 @@ export const makeTrpcIt = <
|
|
|
27
64
|
type Caller = CallerResult<CreateCaller>;
|
|
28
65
|
type Provided = Layer.Success<TestLayer>;
|
|
29
66
|
|
|
30
|
-
const fixtureIt =
|
|
31
|
-
|
|
32
|
-
{ scope: "worker" },
|
|
33
|
-
// biome-ignore lint/correctness/noEmptyPattern: Vitest fixtures require a destructured context parameter.
|
|
34
|
-
async ({}, { onCleanup }) => {
|
|
35
|
-
const scope = Effect.runSync(Scope.make());
|
|
36
|
-
onCleanup(() => Effect.runPromise(Scope.close(scope, Exit.void)));
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
return await Effect.runPromise(
|
|
40
|
-
Layer.buildWithScope(options.layer, scope),
|
|
41
|
-
);
|
|
42
|
-
} catch (error) {
|
|
43
|
-
await Effect.runPromise(Scope.close(scope, Exit.void));
|
|
44
|
-
throw error;
|
|
45
|
-
}
|
|
46
|
-
},
|
|
67
|
+
const fixtureIt = makeFixtureIt<Provided, Layer.Error<TestLayer>>(
|
|
68
|
+
options.layer,
|
|
47
69
|
);
|
|
48
70
|
|
|
49
71
|
const effectTRPC = makeTrpcTester(
|
|
@@ -53,12 +75,36 @@ export const makeTrpcIt = <
|
|
|
53
75
|
>[1],
|
|
54
76
|
);
|
|
55
77
|
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
return withEffectTRPC(effectTRPC) as TrpcIt<Options, Caller, Provided>;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const makeTrpcHarnessIt = <
|
|
82
|
+
CreateCaller extends (...arguments_: never[]) => object,
|
|
83
|
+
// biome-ignore lint/suspicious/noExplicitAny: Layer output and error are recovered with Layer utility types
|
|
84
|
+
TestLayer extends Layer.Layer<any, any, never>,
|
|
85
|
+
Harness,
|
|
86
|
+
>(
|
|
87
|
+
options: MakeTrpcHarnessItOptions<CreateCaller, TestLayer, Harness>,
|
|
88
|
+
): TrpcHarnessIt<Harness, Layer.Success<TestLayer>> => {
|
|
89
|
+
type Options = CallerOptions<CreateCaller>;
|
|
90
|
+
type Caller = CallerResult<CreateCaller>;
|
|
91
|
+
type Provided = Layer.Success<TestLayer>;
|
|
92
|
+
|
|
93
|
+
const fixtureIt = makeFixtureIt<Provided, Layer.Error<TestLayer>>(
|
|
94
|
+
options.layer,
|
|
95
|
+
);
|
|
96
|
+
const effectTRPC = makeTrpcHarnessTester(
|
|
97
|
+
fixtureIt,
|
|
98
|
+
options as unknown as Parameters<
|
|
99
|
+
typeof makeTrpcHarnessTester<
|
|
100
|
+
Options,
|
|
101
|
+
Caller,
|
|
102
|
+
Harness,
|
|
103
|
+
Provided,
|
|
104
|
+
Layer.Error<TestLayer>
|
|
105
|
+
>
|
|
106
|
+
>[1],
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
return withEffectTRPC(effectTRPC);
|
|
64
110
|
};
|
package/src/testing.ts
CHANGED
|
@@ -5,9 +5,15 @@ export {
|
|
|
5
5
|
makeEffectCallerFactory,
|
|
6
6
|
} from "./testing/caller.js";
|
|
7
7
|
export type {
|
|
8
|
+
CallerOptions,
|
|
9
|
+
CallerResult,
|
|
10
|
+
MakeTrpcHarnessItOptions,
|
|
8
11
|
MakeTrpcItOptions,
|
|
12
|
+
TrpcHarnessIt,
|
|
13
|
+
TrpcHarnessTest,
|
|
14
|
+
TrpcHarnessTester,
|
|
9
15
|
TrpcIt,
|
|
10
16
|
TrpcTest,
|
|
11
17
|
TrpcTester,
|
|
12
18
|
} from "./testing/types.js";
|
|
13
|
-
export { makeTrpcIt } from "./testing/vitest.js";
|
|
19
|
+
export { makeTrpcHarnessIt, makeTrpcIt } from "./testing/vitest.js";
|
package/src/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { TRPCError } from "@trpc/server";
|
|
2
|
-
import type { Effect } from "effect";
|
|
2
|
+
import type { Effect, Stream } from "effect";
|
|
3
3
|
|
|
4
|
-
export type ProcedureKind = "mutation" | "query";
|
|
4
|
+
export type ProcedureKind = "mutation" | "query" | "subscription";
|
|
5
5
|
|
|
6
6
|
export interface ProcedureInfo {
|
|
7
7
|
readonly captureStackTrace: () => string | undefined;
|
|
@@ -22,3 +22,8 @@ export type EffectTRPCInstrument = <A, E, R>(
|
|
|
22
22
|
effect: Effect.Effect<A, E, R>,
|
|
23
23
|
procedure: ProcedureInfo,
|
|
24
24
|
) => Effect.Effect<A, E, R>;
|
|
25
|
+
|
|
26
|
+
export type EffectTRPCStreamInstrument = <A, E, R>(
|
|
27
|
+
stream: Stream.Stream<A, E, R>,
|
|
28
|
+
procedure: ProcedureInfo,
|
|
29
|
+
) => Stream.Stream<A, E, R>;
|