@shivaedev/effect-trpc 0.3.4 → 0.4.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 +21 -1
- package/README.md +76 -0
- package/dist/adapter.d.ts +6 -5
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +2 -1
- package/dist/adapter.js.map +1 -1
- package/dist/client/rejection.d.ts +10 -0
- package/dist/client/rejection.d.ts.map +1 -0
- package/dist/client/rejection.js +9 -0
- package/dist/client/rejection.js.map +1 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/context-bridge.d.ts.map +1 -1
- package/dist/internal/context-bridge.js +1 -2
- package/dist/internal/context-bridge.js.map +1 -1
- package/dist/internal/loose.d.ts +4 -0
- package/dist/internal/loose.d.ts.map +1 -0
- package/dist/internal/loose.js +7 -0
- package/dist/internal/loose.js.map +1 -0
- package/dist/internal/procedure-handler.d.ts +5 -12
- package/dist/internal/procedure-handler.d.ts.map +1 -1
- package/dist/internal/procedure-handler.js +3 -4
- package/dist/internal/procedure-handler.js.map +1 -1
- package/dist/internal/procedure-types.d.ts +17 -4
- package/dist/internal/procedure-types.d.ts.map +1 -1
- package/dist/internal/runtime.d.ts +4 -4
- package/dist/internal/runtime.d.ts.map +1 -1
- package/dist/internal/runtime.js +5 -13
- package/dist/internal/runtime.js.map +1 -1
- package/dist/internal/subscription-handler.d.ts +5 -12
- package/dist/internal/subscription-handler.d.ts.map +1 -1
- package/dist/internal/subscription-handler.js +5 -6
- package/dist/internal/subscription-handler.js.map +1 -1
- package/dist/procedure.d.ts +6 -8
- package/dist/procedure.d.ts.map +1 -1
- package/dist/procedure.js +8 -17
- package/dist/procedure.js.map +1 -1
- package/dist/rejection-formatter.d.ts +18 -0
- package/dist/rejection-formatter.d.ts.map +1 -0
- package/dist/rejection-formatter.js +21 -0
- package/dist/rejection-formatter.js.map +1 -0
- package/dist/rejection.d.ts +15 -0
- package/dist/rejection.d.ts.map +1 -0
- package/dist/rejection.js +31 -0
- package/dist/rejection.js.map +1 -0
- package/dist/request-services.d.ts.map +1 -1
- package/dist/request-services.js.map +1 -1
- package/dist/request-signal.d.ts.map +1 -1
- package/dist/request-signal.js.map +1 -1
- package/dist/testing/any-test-layer.d.ts +3 -0
- package/dist/testing/any-test-layer.d.ts.map +1 -0
- package/dist/testing/any-test-layer.js +2 -0
- package/dist/testing/any-test-layer.js.map +1 -0
- package/dist/testing/caller.d.ts +5 -3
- package/dist/testing/caller.d.ts.map +1 -1
- package/dist/testing/caller.js +12 -11
- package/dist/testing/caller.js.map +1 -1
- package/dist/testing/types.d.ts +5 -4
- package/dist/testing/types.d.ts.map +1 -1
- package/dist/testing/vitest.d.ts +4 -3
- package/dist/testing/vitest.d.ts.map +1 -1
- package/dist/testing/vitest.js +13 -14
- package/dist/testing/vitest.js.map +1 -1
- package/dist/testing.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -2
- package/src/adapter.ts +24 -76
- package/src/client/rejection.ts +16 -0
- package/src/client.ts +1 -0
- package/src/errors.ts +8 -19
- package/src/index.ts +9 -6
- package/src/internal/context-bridge.ts +3 -10
- package/src/internal/loose.ts +14 -0
- package/src/internal/procedure-handler.ts +11 -34
- package/src/internal/procedure-types.ts +37 -50
- package/src/internal/runtime.ts +14 -49
- package/src/internal/subscription-handler.ts +15 -47
- package/src/procedure.ts +33 -142
- package/src/rejection-formatter.ts +29 -0
- package/src/rejection.ts +47 -0
- package/src/request-services.ts +5 -25
- package/src/request-signal.ts +1 -4
- package/src/testing/any-test-layer.ts +3 -0
- package/src/testing/caller.ts +29 -32
- package/src/testing/types.ts +15 -47
- package/src/testing/vitest.ts +19 -51
- package/src/testing.ts +3 -3
- package/src/types.ts +3 -12
package/src/adapter.ts
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
inferProcedureBuilderResolverOptions,
|
|
3
|
-
TRPCProcedureBuilder,
|
|
4
|
-
} from "@trpc/server";
|
|
1
|
+
import type { TRPCProcedureBuilder } from "@trpc/server";
|
|
5
2
|
import type { Context, Effect, Exit } from "effect";
|
|
6
|
-
import { makeContextBridge } from "./internal/context-bridge.
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
EffectTRPCStreamInstrument,
|
|
14
|
-
} from "./types.js";
|
|
3
|
+
import { makeContextBridge } from "./internal/context-bridge.ts";
|
|
4
|
+
import { looseBuilder } from "./internal/loose.ts";
|
|
5
|
+
import type { ResolverContext } from "./internal/procedure-types.ts";
|
|
6
|
+
import { makeRuntimeBridge } from "./internal/runtime.ts";
|
|
7
|
+
import { EffectProcedureBuilder } from "./procedure.ts";
|
|
8
|
+
import type { EffectProcedureRequestServices } from "./request-services.ts";
|
|
9
|
+
import type { EffectTRPCErrorMapper, EffectTRPCInstrument, EffectTRPCStreamInstrument } from "./types.ts";
|
|
15
10
|
|
|
16
11
|
export interface MakeEffectTRPCOptions<Requirements, RuntimeError> {
|
|
17
12
|
readonly instrument?: EffectTRPCInstrument;
|
|
@@ -21,10 +16,7 @@ export interface MakeEffectTRPCOptions<Requirements, RuntimeError> {
|
|
|
21
16
|
}
|
|
22
17
|
|
|
23
18
|
export interface EffectTRPCRuntime<Requirements, RuntimeError> {
|
|
24
|
-
readonly contextEffect: Effect.Effect<
|
|
25
|
-
Context.Context<Requirements>,
|
|
26
|
-
RuntimeError
|
|
27
|
-
>;
|
|
19
|
+
readonly contextEffect: Effect.Effect<Context.Context<Requirements>, RuntimeError>;
|
|
28
20
|
readonly currentServices?: () => Context.Context<never> | undefined;
|
|
29
21
|
readonly runPromise: <Value, Error>(
|
|
30
22
|
effect: Effect.Effect<Value, Error, Requirements>,
|
|
@@ -34,66 +26,15 @@ export interface EffectTRPCRuntime<Requirements, RuntimeError> {
|
|
|
34
26
|
effect: Effect.Effect<Value, Error, Requirements>,
|
|
35
27
|
options?: { readonly signal?: AbortSignal },
|
|
36
28
|
) => Promise<Exit.Exit<Value, Error | RuntimeError>>;
|
|
37
|
-
readonly runWithServices?: <Services, Value>(
|
|
38
|
-
services: Context.Context<Services>,
|
|
39
|
-
evaluate: () => Value,
|
|
40
|
-
) => Value;
|
|
29
|
+
readonly runWithServices?: <Services, Value>(services: Context.Context<Services>, evaluate: () => Value) => Value;
|
|
41
30
|
}
|
|
42
31
|
|
|
43
32
|
export interface EffectTRPCAdapter<Requirements> {
|
|
44
|
-
readonly procedure: <
|
|
45
|
-
Context,
|
|
46
|
-
Meta,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
InputOut,
|
|
50
|
-
OutputIn,
|
|
51
|
-
OutputOut,
|
|
52
|
-
ProvidedServices,
|
|
53
|
-
LayerError,
|
|
54
|
-
>(
|
|
55
|
-
builder: TRPCProcedureBuilder<
|
|
56
|
-
Context,
|
|
57
|
-
Meta,
|
|
58
|
-
ContextOverrides,
|
|
59
|
-
InputIn,
|
|
60
|
-
InputOut,
|
|
61
|
-
OutputIn,
|
|
62
|
-
OutputOut,
|
|
63
|
-
false
|
|
64
|
-
>,
|
|
65
|
-
requestServices: EffectProcedureRequestServices<
|
|
66
|
-
inferProcedureBuilderResolverOptions<
|
|
67
|
-
TRPCProcedureBuilder<
|
|
68
|
-
Context,
|
|
69
|
-
Meta,
|
|
70
|
-
ContextOverrides,
|
|
71
|
-
InputIn,
|
|
72
|
-
InputOut,
|
|
73
|
-
OutputIn,
|
|
74
|
-
OutputOut,
|
|
75
|
-
false
|
|
76
|
-
>
|
|
77
|
-
>["ctx"],
|
|
78
|
-
ProvidedServices,
|
|
79
|
-
LayerError
|
|
80
|
-
>,
|
|
81
|
-
) => EffectProcedureBuilder<
|
|
82
|
-
Context,
|
|
83
|
-
Meta,
|
|
84
|
-
ContextOverrides,
|
|
85
|
-
InputIn,
|
|
86
|
-
InputOut,
|
|
87
|
-
OutputIn,
|
|
88
|
-
OutputOut,
|
|
89
|
-
ProvidedServices,
|
|
90
|
-
LayerError,
|
|
91
|
-
Requirements
|
|
92
|
-
>;
|
|
93
|
-
readonly runWithServices: <Services, Value>(
|
|
94
|
-
services: Context.Context<Services>,
|
|
95
|
-
evaluate: () => Value,
|
|
96
|
-
) => Value;
|
|
33
|
+
readonly procedure: <Context, Meta, ContextOverrides, InputIn, InputOut, OutputIn, OutputOut, ProvidedServices, LayerError>(
|
|
34
|
+
builder: TRPCProcedureBuilder<Context, Meta, ContextOverrides, InputIn, InputOut, OutputIn, OutputOut, false>,
|
|
35
|
+
requestServices: EffectProcedureRequestServices<ResolverContext<Context, Meta, ContextOverrides>, ProvidedServices, LayerError>,
|
|
36
|
+
) => EffectProcedureBuilder<Context, Meta, ContextOverrides, InputIn, InputOut, OutputIn, OutputOut, ProvidedServices, LayerError, Requirements>;
|
|
37
|
+
readonly runWithServices: <Services, Value>(services: Context.Context<Services>, evaluate: () => Value) => Value;
|
|
97
38
|
}
|
|
98
39
|
|
|
99
40
|
export const makeEffectTRPC = <Requirements, RuntimeError = never>(
|
|
@@ -103,8 +44,15 @@ export const makeEffectTRPC = <Requirements, RuntimeError = never>(
|
|
|
103
44
|
const runtime = makeRuntimeBridge(options.runtime, contextBridge, options);
|
|
104
45
|
|
|
105
46
|
return {
|
|
106
|
-
procedure:
|
|
107
|
-
|
|
47
|
+
procedure: <Context, Meta, ContextOverrides, InputIn, InputOut, OutputIn, OutputOut, ProvidedServices, LayerError>(
|
|
48
|
+
builder: TRPCProcedureBuilder<Context, Meta, ContextOverrides, InputIn, InputOut, OutputIn, OutputOut, false>,
|
|
49
|
+
requestServices: EffectProcedureRequestServices<ResolverContext<Context, Meta, ContextOverrides>, ProvidedServices, LayerError>,
|
|
50
|
+
) =>
|
|
51
|
+
new EffectProcedureBuilder<Context, Meta, ContextOverrides, InputIn, InputOut, OutputIn, OutputOut, ProvidedServices, LayerError, Requirements>(
|
|
52
|
+
looseBuilder(builder),
|
|
53
|
+
requestServices,
|
|
54
|
+
runtime,
|
|
55
|
+
),
|
|
108
56
|
runWithServices: contextBridge.run,
|
|
109
57
|
};
|
|
110
58
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Option, Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
export const EncodedRejection = Schema.StructWithRest(Schema.Struct({ _tag: Schema.String }), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
4
|
+
|
|
5
|
+
export type EncodedRejection = typeof EncodedRejection.Type;
|
|
6
|
+
|
|
7
|
+
const decodeCarrier = Schema.decodeUnknownOption(Schema.Struct({ data: Schema.Struct({ rejection: EncodedRejection }) }));
|
|
8
|
+
|
|
9
|
+
export const rejectionOf = (error: unknown): Option.Option<EncodedRejection> => Option.map(decodeCarrier(error), ({ data }) => data.rejection);
|
|
10
|
+
|
|
11
|
+
export const decodeRejection = <S extends Schema.ConstraintDecoder<{ readonly _tag: string }>>(
|
|
12
|
+
schema: S,
|
|
13
|
+
): ((error: unknown) => Option.Option<S["Type"]>) => {
|
|
14
|
+
const decode = Schema.decodeUnknownOption(schema);
|
|
15
|
+
return (error) => Option.flatMap(rejectionOf(error), (rejection) => decode(rejection));
|
|
16
|
+
};
|
package/src/client.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { decodeRejection, type EncodedRejection, rejectionOf } from "./client/rejection.ts";
|
package/src/errors.ts
CHANGED
|
@@ -1,30 +1,19 @@
|
|
|
1
1
|
import { TRPCError } from "@trpc/server";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
3
|
|
|
4
|
-
export const fail = (
|
|
5
|
-
code: TRPCError["code"],
|
|
6
|
-
message: string,
|
|
7
|
-
cause?: unknown,
|
|
8
|
-
): Effect.Effect<never, TRPCError> =>
|
|
4
|
+
export const fail = (code: TRPCError["code"], message: string, cause?: unknown): Effect.Effect<never, TRPCError> =>
|
|
9
5
|
Effect.fail(new TRPCError({ cause, code, message }));
|
|
10
6
|
|
|
11
|
-
export const badRequest = (message: string, cause?: unknown) =>
|
|
12
|
-
fail("BAD_REQUEST", message, cause);
|
|
7
|
+
export const badRequest = (message: string, cause?: unknown) => fail("BAD_REQUEST", message, cause);
|
|
13
8
|
|
|
14
|
-
export const unauthorized = (message: string, cause?: unknown) =>
|
|
15
|
-
fail("UNAUTHORIZED", message, cause);
|
|
9
|
+
export const unauthorized = (message: string, cause?: unknown) => fail("UNAUTHORIZED", message, cause);
|
|
16
10
|
|
|
17
|
-
export const forbidden = (message: string, cause?: unknown) =>
|
|
18
|
-
fail("FORBIDDEN", message, cause);
|
|
11
|
+
export const forbidden = (message: string, cause?: unknown) => fail("FORBIDDEN", message, cause);
|
|
19
12
|
|
|
20
|
-
export const notFound = (message: string, cause?: unknown) =>
|
|
21
|
-
fail("NOT_FOUND", message, cause);
|
|
13
|
+
export const notFound = (message: string, cause?: unknown) => fail("NOT_FOUND", message, cause);
|
|
22
14
|
|
|
23
|
-
export const conflict = (message: string, cause?: unknown) =>
|
|
24
|
-
fail("CONFLICT", message, cause);
|
|
15
|
+
export const conflict = (message: string, cause?: unknown) => fail("CONFLICT", message, cause);
|
|
25
16
|
|
|
26
|
-
export const preconditionFailed = (message: string, cause?: unknown) =>
|
|
27
|
-
fail("PRECONDITION_FAILED", message, cause);
|
|
17
|
+
export const preconditionFailed = (message: string, cause?: unknown) => fail("PRECONDITION_FAILED", message, cause);
|
|
28
18
|
|
|
29
|
-
export const internalServerError = (message: string, cause?: unknown) =>
|
|
30
|
-
fail("INTERNAL_SERVER_ERROR", message, cause);
|
|
19
|
+
export const internalServerError = (message: string, cause?: unknown) => fail("INTERNAL_SERVER_ERROR", message, cause);
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,8 @@ export {
|
|
|
3
3
|
type EffectTRPCRuntime,
|
|
4
4
|
type MakeEffectTRPCOptions,
|
|
5
5
|
makeEffectTRPC,
|
|
6
|
-
} from "./adapter.
|
|
6
|
+
} from "./adapter.ts";
|
|
7
|
+
export type { EncodedRejection } from "./client/rejection.ts";
|
|
7
8
|
export {
|
|
8
9
|
badRequest,
|
|
9
10
|
conflict,
|
|
@@ -13,14 +14,16 @@ export {
|
|
|
13
14
|
notFound,
|
|
14
15
|
preconditionFailed,
|
|
15
16
|
unauthorized,
|
|
16
|
-
} from "./errors.
|
|
17
|
-
export type { EffectProcedureBuilder } from "./procedure.
|
|
17
|
+
} from "./errors.ts";
|
|
18
|
+
export type { EffectProcedureBuilder } from "./procedure.ts";
|
|
19
|
+
export { RejectionError, type RejectWithOptions, rejectionCode, rejectWith } from "./rejection.ts";
|
|
20
|
+
export { type RejectionData, type RejectionErrorShape, rejectionFormatter, withRejection } from "./rejection-formatter.ts";
|
|
18
21
|
export {
|
|
19
22
|
type EffectProcedureRequestServices,
|
|
20
23
|
extendRequestServices,
|
|
21
24
|
makeRequestServices,
|
|
22
|
-
} from "./request-services.
|
|
23
|
-
export { RequestSignal } from "./request-signal.
|
|
25
|
+
} from "./request-services.ts";
|
|
26
|
+
export { RequestSignal } from "./request-signal.ts";
|
|
24
27
|
export type {
|
|
25
28
|
EffectTRPCErrorContext,
|
|
26
29
|
EffectTRPCErrorMapper,
|
|
@@ -28,4 +31,4 @@ export type {
|
|
|
28
31
|
EffectTRPCStreamInstrument,
|
|
29
32
|
ProcedureInfo,
|
|
30
33
|
ProcedureKind,
|
|
31
|
-
} from "./types.
|
|
34
|
+
} from "./types.ts";
|
|
@@ -3,20 +3,14 @@ import type { Context } from "effect";
|
|
|
3
3
|
|
|
4
4
|
export interface ContextBridge {
|
|
5
5
|
readonly current: () => Context.Context<never> | undefined;
|
|
6
|
-
readonly run: <Services, Value>(
|
|
7
|
-
services: Context.Context<Services>,
|
|
8
|
-
evaluate: () => Value,
|
|
9
|
-
) => Value;
|
|
6
|
+
readonly run: <Services, Value>(services: Context.Context<Services>, evaluate: () => Value) => Value;
|
|
10
7
|
}
|
|
11
8
|
|
|
12
9
|
export const makeContextBridge = (shared?: {
|
|
13
10
|
readonly currentServices?: ContextBridge["current"];
|
|
14
11
|
readonly runWithServices?: ContextBridge["run"];
|
|
15
12
|
}): ContextBridge => {
|
|
16
|
-
if (
|
|
17
|
-
shared?.currentServices !== undefined &&
|
|
18
|
-
shared.runWithServices !== undefined
|
|
19
|
-
) {
|
|
13
|
+
if (shared?.currentServices !== undefined && shared.runWithServices !== undefined) {
|
|
20
14
|
return {
|
|
21
15
|
current: shared.currentServices,
|
|
22
16
|
run: shared.runWithServices,
|
|
@@ -27,7 +21,6 @@ export const makeContextBridge = (shared?: {
|
|
|
27
21
|
|
|
28
22
|
return {
|
|
29
23
|
current: () => storage.getStore(),
|
|
30
|
-
run: (services, evaluate) =>
|
|
31
|
-
storage.run(services as Context.Context<never>, evaluate),
|
|
24
|
+
run: (services, evaluate) => storage.run(services, evaluate),
|
|
32
25
|
};
|
|
33
26
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ProcedureBuilderOf, ProcedureBuilderSurface, ResolverContext } from "./procedure-types.ts";
|
|
2
|
+
|
|
3
|
+
export function looseBuilder<Context, Meta, ContextOverrides, InputIn, InputOut, OutputIn, OutputOut>(
|
|
4
|
+
builder: ProcedureBuilderOf<Context, Meta, ContextOverrides, InputIn, InputOut, OutputIn, OutputOut>,
|
|
5
|
+
): ProcedureBuilderSurface<ResolverContext<Context, Meta, ContextOverrides>>;
|
|
6
|
+
export function looseBuilder(builder: unknown): unknown {
|
|
7
|
+
return builder;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// tRPC runs the procedure's input parsers before the resolver, so the resolver receives the input it declared.
|
|
11
|
+
export function acceptParsedInput<Result>(resolver: (input: never) => Result): (input: unknown) => Result;
|
|
12
|
+
export function acceptParsedInput(resolver: unknown): unknown {
|
|
13
|
+
return resolver;
|
|
14
|
+
}
|
|
@@ -1,45 +1,22 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import type { EffectProcedureRequestServices } from "../request-services.
|
|
3
|
-
import type { ProcedureInfo } from "../types.
|
|
4
|
-
import
|
|
5
|
-
import type {
|
|
2
|
+
import type { EffectProcedureRequestServices } from "../request-services.ts";
|
|
3
|
+
import type { ProcedureInfo } from "../types.ts";
|
|
4
|
+
import { acceptParsedInput } from "./loose.ts";
|
|
5
|
+
import type { EffectProcedureResolver, ProcedureInvocation } from "./procedure-types.ts";
|
|
6
|
+
import type { RuntimeBridge } from "./runtime.ts";
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
readonly ctx: Context;
|
|
9
|
-
readonly input: Input;
|
|
10
|
-
readonly path: string;
|
|
11
|
-
readonly signal?: AbortSignal;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const makeProcedureHandler = <
|
|
15
|
-
RuntimeRequirements,
|
|
16
|
-
Context,
|
|
17
|
-
Input,
|
|
18
|
-
ProvidedServices,
|
|
19
|
-
LayerError,
|
|
20
|
-
Output,
|
|
21
|
-
>(
|
|
8
|
+
export const makeProcedureHandler = <RuntimeRequirements, Context, ProvidedServices, LayerError>(
|
|
22
9
|
runtime: RuntimeBridge<RuntimeRequirements>,
|
|
23
|
-
resolver: EffectProcedureResolver<
|
|
24
|
-
|
|
25
|
-
ProvidedServices | NoInfer<RuntimeRequirements>,
|
|
26
|
-
Output
|
|
27
|
-
>,
|
|
28
|
-
requestServices: EffectProcedureRequestServices<
|
|
29
|
-
Context,
|
|
30
|
-
ProvidedServices,
|
|
31
|
-
LayerError
|
|
32
|
-
>,
|
|
10
|
+
resolver: EffectProcedureResolver<never, ProvidedServices | NoInfer<RuntimeRequirements>, unknown>,
|
|
11
|
+
requestServices: EffectProcedureRequestServices<Context, ProvidedServices, LayerError>,
|
|
33
12
|
procedure: Omit<ProcedureInfo, "path">,
|
|
34
13
|
) => {
|
|
35
|
-
const tracedResolver = Effect.fnUntraced(resolver);
|
|
14
|
+
const tracedResolver = Effect.fnUntraced(acceptParsedInput(resolver));
|
|
36
15
|
|
|
37
|
-
return async (invocation: ProcedureInvocation<Context
|
|
16
|
+
return async (invocation: ProcedureInvocation<Context>) => {
|
|
38
17
|
const info = { ...procedure, path: invocation.path };
|
|
39
18
|
const provided = Effect.suspend(() =>
|
|
40
|
-
runtime
|
|
41
|
-
.instrument(tracedResolver(invocation.input), info)
|
|
42
|
-
.pipe(Effect.provide(requestServices.layer(invocation.ctx))),
|
|
19
|
+
runtime.instrument(tracedResolver(invocation.input), info).pipe(Effect.provide(requestServices.layer(invocation.ctx))),
|
|
43
20
|
);
|
|
44
21
|
return await runtime.runEffect(provided, {
|
|
45
22
|
procedure: info,
|
|
@@ -1,57 +1,44 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
any,
|
|
13
|
-
// biome-ignore lint/suspicious/noExplicitAny: tRPC phantom slot extraction
|
|
14
|
-
any,
|
|
15
|
-
// biome-ignore lint/suspicious/noExplicitAny: tRPC phantom slot extraction
|
|
16
|
-
any,
|
|
17
|
-
// biome-ignore lint/suspicious/noExplicitAny: tRPC phantom slot extraction
|
|
18
|
-
any,
|
|
19
|
-
// biome-ignore lint/suspicious/noExplicitAny: tRPC phantom slot extraction
|
|
20
|
-
any,
|
|
21
|
-
// biome-ignore lint/suspicious/noExplicitAny: tRPC phantom slot extraction
|
|
22
|
-
any,
|
|
1
|
+
import type { inferProcedureBuilderResolverOptions, TRPCProcedureBuilder, TRPCUnsetMarker } from "@trpc/server";
|
|
2
|
+
import type { Effect, StandardSchema, Stream } from "effect";
|
|
3
|
+
|
|
4
|
+
export type ProcedureBuilderOf<Context, Meta, ContextOverrides, InputIn, InputOut, OutputIn, OutputOut> = TRPCProcedureBuilder<
|
|
5
|
+
Context,
|
|
6
|
+
Meta,
|
|
7
|
+
ContextOverrides,
|
|
8
|
+
InputIn,
|
|
9
|
+
InputOut,
|
|
10
|
+
OutputIn,
|
|
11
|
+
OutputOut,
|
|
23
12
|
false
|
|
24
13
|
>;
|
|
25
14
|
|
|
26
|
-
export type DefaultValue<Value, Fallback> = Value extends TRPCUnsetMarker
|
|
27
|
-
? Fallback
|
|
28
|
-
: Value;
|
|
29
|
-
|
|
30
|
-
export type IntersectIfDefined<Value, With> = Value extends TRPCUnsetMarker
|
|
31
|
-
? With
|
|
32
|
-
: With extends TRPCUnsetMarker
|
|
33
|
-
? Value
|
|
34
|
-
: Value & With;
|
|
35
|
-
|
|
36
|
-
export type ResolverContext<Builder extends AnyProcedureBuilder> =
|
|
37
|
-
inferProcedureBuilderResolverOptions<Builder>["ctx"];
|
|
38
|
-
|
|
39
|
-
export type ProcedureEffect<Requirements> = Effect.Effect<
|
|
40
|
-
// biome-ignore lint/suspicious/noExplicitAny: success is inferred from each yielded Effect
|
|
41
|
-
any,
|
|
42
|
-
// biome-ignore lint/suspicious/noExplicitAny: errors are inferred from each yielded Effect
|
|
43
|
-
any,
|
|
44
|
-
Requirements
|
|
45
|
-
>;
|
|
15
|
+
export type DefaultValue<Value, Fallback> = Value extends TRPCUnsetMarker ? Fallback : Value;
|
|
46
16
|
|
|
47
|
-
export type
|
|
48
|
-
|
|
49
|
-
|
|
17
|
+
export type IntersectIfDefined<Value, With> = Value extends TRPCUnsetMarker ? With : With extends TRPCUnsetMarker ? Value : Value & With;
|
|
18
|
+
|
|
19
|
+
export type ResolverContext<Context, Meta, ContextOverrides> = inferProcedureBuilderResolverOptions<
|
|
20
|
+
ProcedureBuilderOf<Context, Meta, ContextOverrides, TRPCUnsetMarker, TRPCUnsetMarker, TRPCUnsetMarker, TRPCUnsetMarker>
|
|
21
|
+
>["ctx"];
|
|
22
|
+
|
|
23
|
+
export type ProcedureEffect<Requirements> = Effect.Effect<unknown, unknown, Requirements>;
|
|
24
|
+
|
|
25
|
+
export type EffectProcedureResolver<Input, Requirements, Output> = (input: Input) => Generator<ProcedureEffect<Requirements>, Output, never>;
|
|
50
26
|
|
|
51
27
|
export type EffectSubscriptionResolver<Input, Requirements, Output> = (
|
|
52
28
|
input: Input,
|
|
53
|
-
) => Generator<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
29
|
+
) => Generator<ProcedureEffect<Requirements>, Stream.Stream<Output, unknown, Requirements>, never>;
|
|
30
|
+
|
|
31
|
+
export interface ProcedureInvocation<Context> {
|
|
32
|
+
readonly ctx: Context;
|
|
33
|
+
readonly input: unknown;
|
|
34
|
+
readonly path: string;
|
|
35
|
+
readonly signal: AbortSignal | undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ProcedureBuilderSurface<Context> {
|
|
39
|
+
input(schema: StandardSchema.StandardSchemaV1): ProcedureBuilderSurface<Context>;
|
|
40
|
+
output(schema: StandardSchema.StandardSchemaV1): ProcedureBuilderSurface<Context>;
|
|
41
|
+
query(resolver: (invocation: ProcedureInvocation<Context>) => Promise<unknown>): unknown;
|
|
42
|
+
mutation(resolver: (invocation: ProcedureInvocation<Context>) => Promise<unknown>): unknown;
|
|
43
|
+
subscription(resolver: (invocation: ProcedureInvocation<Context>) => Promise<AsyncIterable<unknown, void, unknown>>): unknown;
|
|
44
|
+
}
|
package/src/internal/runtime.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { TRPCError } from "@trpc/server";
|
|
2
2
|
import { Cause, Context, Effect, Exit, Option, Result, Stream } from "effect";
|
|
3
|
-
import type { EffectTRPCRuntime } from "../adapter.
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
EffectTRPCInstrument,
|
|
7
|
-
ProcedureInfo,
|
|
8
|
-
} from "../types.js";
|
|
9
|
-
import type { ContextBridge } from "./context-bridge.js";
|
|
3
|
+
import type { EffectTRPCRuntime } from "../adapter.ts";
|
|
4
|
+
import type { EffectTRPCErrorMapper, EffectTRPCInstrument, ProcedureInfo } from "../types.ts";
|
|
5
|
+
import type { ContextBridge } from "./context-bridge.ts";
|
|
10
6
|
|
|
11
7
|
interface RunEffectOptions {
|
|
12
8
|
readonly procedure: ProcedureInfo;
|
|
@@ -15,14 +11,8 @@ interface RunEffectOptions {
|
|
|
15
11
|
|
|
16
12
|
export interface RuntimeBridge<Requirements> {
|
|
17
13
|
readonly instrument: EffectTRPCInstrument;
|
|
18
|
-
readonly runEffect: <Value>(
|
|
19
|
-
|
|
20
|
-
options: RunEffectOptions,
|
|
21
|
-
) => Promise<Value>;
|
|
22
|
-
readonly runStream: <Value>(
|
|
23
|
-
stream: Stream.Stream<Value, unknown, Requirements>,
|
|
24
|
-
options: RunEffectOptions,
|
|
25
|
-
) => Promise<AsyncIterable<Value>>;
|
|
14
|
+
readonly runEffect: <Value>(effect: Effect.Effect<Value, unknown, Requirements>, options: RunEffectOptions) => Promise<Value>;
|
|
15
|
+
readonly runStream: <Value>(stream: Stream.Stream<Value, unknown, Requirements>, options: RunEffectOptions) => Promise<AsyncIterable<Value>>;
|
|
26
16
|
}
|
|
27
17
|
|
|
28
18
|
const internalError = (cause?: unknown): TRPCError =>
|
|
@@ -43,19 +33,13 @@ const mapError = (
|
|
|
43
33
|
}
|
|
44
34
|
|
|
45
35
|
try {
|
|
46
|
-
return (
|
|
47
|
-
consumerMapper?.(error, { ...procedure, origin }) ?? internalError(error)
|
|
48
|
-
);
|
|
36
|
+
return consumerMapper?.(error, { ...procedure, origin }) ?? internalError(error);
|
|
49
37
|
} catch (mapperDefect) {
|
|
50
38
|
return internalError(mapperDefect);
|
|
51
39
|
}
|
|
52
40
|
};
|
|
53
41
|
|
|
54
|
-
const mapCause = (
|
|
55
|
-
cause: Cause.Cause<unknown>,
|
|
56
|
-
procedure: ProcedureInfo,
|
|
57
|
-
consumerMapper: EffectTRPCErrorMapper | undefined,
|
|
58
|
-
): TRPCError => {
|
|
42
|
+
const mapCause = (cause: Cause.Cause<unknown>, procedure: ProcedureInfo, consumerMapper: EffectTRPCErrorMapper | undefined): TRPCError => {
|
|
59
43
|
if (Cause.hasInterruptsOnly(cause)) {
|
|
60
44
|
return new TRPCError({
|
|
61
45
|
code: "CLIENT_CLOSED_REQUEST",
|
|
@@ -94,21 +78,14 @@ export const makeRuntimeBridge = <Requirements, RuntimeError>(
|
|
|
94
78
|
contextBridge: ContextBridge,
|
|
95
79
|
options: {
|
|
96
80
|
readonly instrument?: EffectTRPCInstrument;
|
|
97
|
-
readonly instrumentStream?: import("../types.
|
|
81
|
+
readonly instrumentStream?: import("../types.ts").EffectTRPCStreamInstrument;
|
|
98
82
|
readonly mapError?: EffectTRPCErrorMapper;
|
|
99
83
|
},
|
|
100
84
|
): RuntimeBridge<Requirements> => ({
|
|
101
|
-
instrument: (effect, procedure) =>
|
|
102
|
-
Effect.suspend(() =>
|
|
103
|
-
options.instrument === undefined
|
|
104
|
-
? effect
|
|
105
|
-
: options.instrument(effect, procedure),
|
|
106
|
-
),
|
|
85
|
+
instrument: (effect, procedure) => Effect.suspend(() => (options.instrument === undefined ? effect : options.instrument(effect, procedure))),
|
|
107
86
|
runStream: async (stream, runOptions) => {
|
|
108
87
|
const instrumented = Stream.suspend(() =>
|
|
109
|
-
options.instrumentStream === undefined
|
|
110
|
-
? stream
|
|
111
|
-
: options.instrumentStream(stream, runOptions.procedure),
|
|
88
|
+
options.instrumentStream === undefined ? stream : options.instrumentStream(stream, runOptions.procedure),
|
|
112
89
|
).pipe(
|
|
113
90
|
Stream.withSpan(runOptions.procedure.path, {
|
|
114
91
|
attributes: {
|
|
@@ -119,14 +96,11 @@ export const makeRuntimeBridge = <Requirements, RuntimeError>(
|
|
|
119
96
|
captureStackTrace: runOptions.procedure.captureStackTrace,
|
|
120
97
|
}),
|
|
121
98
|
Stream.interruptWhen(interruptOn(runOptions.signal)),
|
|
122
|
-
Stream.catchCause((cause) =>
|
|
123
|
-
Stream.fail(mapCause(cause, runOptions.procedure, options.mapError)),
|
|
124
|
-
),
|
|
99
|
+
Stream.catchCause((cause) => Stream.fail(mapCause(cause, runOptions.procedure, options.mapError))),
|
|
125
100
|
);
|
|
126
101
|
const context = await runtime.runPromise(Effect.context<Requirements>());
|
|
127
102
|
const ambient = contextBridge.current();
|
|
128
|
-
const provided =
|
|
129
|
-
ambient === undefined ? context : Context.merge(context, ambient);
|
|
103
|
+
const provided = ambient === undefined ? context : Context.merge(context, ambient);
|
|
130
104
|
return Stream.toAsyncIterableWith(instrumented, provided);
|
|
131
105
|
},
|
|
132
106
|
runEffect: async (effect, runOptions) => {
|
|
@@ -147,17 +121,8 @@ export const makeRuntimeBridge = <Requirements, RuntimeError>(
|
|
|
147
121
|
);
|
|
148
122
|
const ambient = contextBridge.current();
|
|
149
123
|
const runnable =
|
|
150
|
-
ambient === undefined
|
|
151
|
-
|
|
152
|
-
: Effect.flatMap(runtime.contextEffect, (base) =>
|
|
153
|
-
Effect.provideContext(traced, Context.merge(base, ambient)),
|
|
154
|
-
);
|
|
155
|
-
const exit = await runtime.runPromiseExit(
|
|
156
|
-
runnable,
|
|
157
|
-
runOptions.signal === undefined
|
|
158
|
-
? undefined
|
|
159
|
-
: { signal: runOptions.signal },
|
|
160
|
-
);
|
|
124
|
+
ambient === undefined ? traced : Effect.flatMap(runtime.contextEffect, (base) => Effect.provideContext(traced, Context.merge(base, ambient)));
|
|
125
|
+
const exit = await runtime.runPromiseExit(runnable, runOptions.signal === undefined ? undefined : { signal: runOptions.signal });
|
|
161
126
|
|
|
162
127
|
if (Exit.isSuccess(exit)) {
|
|
163
128
|
return exit.value;
|
|
@@ -1,59 +1,27 @@
|
|
|
1
1
|
import { Effect, Layer, Schema, Stream } from "effect";
|
|
2
|
-
import type { EffectProcedureRequestServices } from "../request-services.
|
|
3
|
-
import { RequestSignal } from "../request-signal.
|
|
4
|
-
import type { ProcedureInfo } from "../types.
|
|
5
|
-
import
|
|
6
|
-
import type {
|
|
2
|
+
import type { EffectProcedureRequestServices } from "../request-services.ts";
|
|
3
|
+
import { RequestSignal } from "../request-signal.ts";
|
|
4
|
+
import type { ProcedureInfo } from "../types.ts";
|
|
5
|
+
import { acceptParsedInput } from "./loose.ts";
|
|
6
|
+
import type { EffectSubscriptionResolver, ProcedureInvocation } from "./procedure-types.ts";
|
|
7
|
+
import type { RuntimeBridge } from "./runtime.ts";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
readonly ctx: Context;
|
|
10
|
-
readonly input: Input;
|
|
11
|
-
readonly path: string;
|
|
12
|
-
readonly signal?: AbortSignal;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const makeSubscriptionHandler = <
|
|
16
|
-
RuntimeRequirements,
|
|
17
|
-
Context,
|
|
18
|
-
Input,
|
|
19
|
-
ProvidedServices,
|
|
20
|
-
LayerError,
|
|
21
|
-
Output,
|
|
22
|
-
>(
|
|
9
|
+
export const makeSubscriptionHandler = <RuntimeRequirements, Context, ProvidedServices, LayerError>(
|
|
23
10
|
runtime: RuntimeBridge<RuntimeRequirements>,
|
|
24
|
-
resolver: EffectSubscriptionResolver<
|
|
25
|
-
|
|
26
|
-
ProvidedServices | NoInfer<RuntimeRequirements>,
|
|
27
|
-
Output
|
|
28
|
-
>,
|
|
29
|
-
requestServices: EffectProcedureRequestServices<
|
|
30
|
-
Context,
|
|
31
|
-
ProvidedServices,
|
|
32
|
-
LayerError
|
|
33
|
-
>,
|
|
11
|
+
resolver: EffectSubscriptionResolver<never, ProvidedServices | NoInfer<RuntimeRequirements>, unknown>,
|
|
12
|
+
requestServices: EffectProcedureRequestServices<Context, ProvidedServices, LayerError>,
|
|
34
13
|
procedure: Omit<ProcedureInfo, "path">,
|
|
35
14
|
outputSchema?: Schema.ConstraintDecoder<unknown>,
|
|
36
15
|
) => {
|
|
37
|
-
const tracedResolver = Effect.fnUntraced(resolver);
|
|
16
|
+
const tracedResolver = Effect.fnUntraced(acceptParsedInput(resolver));
|
|
38
17
|
|
|
39
|
-
return async (invocation:
|
|
18
|
+
return async (invocation: ProcedureInvocation<Context>) => {
|
|
40
19
|
const info = { ...procedure, path: invocation.path };
|
|
41
|
-
const requestLayer = Layer.merge(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
);
|
|
45
|
-
let stream = Stream.unwrap(
|
|
46
|
-
Effect.suspend(() =>
|
|
47
|
-
runtime.instrument(tracedResolver(invocation.input), info),
|
|
48
|
-
),
|
|
49
|
-
).pipe(Stream.provide(requestLayer));
|
|
50
|
-
if (outputSchema !== undefined) {
|
|
51
|
-
stream = Stream.mapEffect(stream, (value) =>
|
|
52
|
-
Schema.decodeUnknownEffect(outputSchema)(value),
|
|
53
|
-
) as typeof stream;
|
|
54
|
-
}
|
|
20
|
+
const requestLayer = Layer.merge(requestServices.layer(invocation.ctx), Layer.succeed(RequestSignal, invocation.signal));
|
|
21
|
+
const stream = Stream.unwrap(Effect.suspend(() => runtime.instrument(tracedResolver(invocation.input), info))).pipe(Stream.provide(requestLayer));
|
|
22
|
+
const decoded = outputSchema === undefined ? stream : Stream.mapEffect(stream, (value) => Schema.decodeUnknownEffect(outputSchema)(value));
|
|
55
23
|
|
|
56
|
-
return await runtime.runStream(
|
|
24
|
+
return await runtime.runStream(decoded, {
|
|
57
25
|
procedure: info,
|
|
58
26
|
...(invocation.signal === undefined ? {} : { signal: invocation.signal }),
|
|
59
27
|
});
|