Middlewares
All checks were successful
Lint / lint (push) Successful in 11s

This commit is contained in:
Julien Valverdé
2024-09-08 01:31:07 +02:00
parent 08bf836267
commit fcb11956da
7 changed files with 94 additions and 54 deletions

View File

@@ -6,7 +6,9 @@ import type { IncomingMessage } from "node:http"
import type { WebSocket } from "ws" import type { WebSocket } from "ws"
export interface TRPCContext<R> { export interface TRPCContext<R> extends TRPCContextRuntime<R>, TRPCContextTransaction {}
export interface TRPCContextRuntime<R> {
readonly runtime: Runtime.Runtime<R> readonly runtime: Runtime.Runtime<R>
readonly run: <A, E>( readonly run: <A, E>(
@@ -18,12 +20,14 @@ export interface TRPCContext<R> {
effect: Effect.Effect<A, E, R>, effect: Effect.Effect<A, E, R>,
options?: Runtime.RunForkOptions, options?: Runtime.RunForkOptions,
) => RuntimeFiber<A, TRPCError> ) => RuntimeFiber<A, TRPCError>
}
readonly transaction: TRPCContextTransaction export interface TRPCContextTransaction {
readonly transaction: TRPCTransaction
} }
export type TRPCContextTransaction = Data.TaggedEnum<{ export type TRPCTransaction = Data.TaggedEnum<{
readonly Express: { readonly Express: {
readonly req: express.Request readonly req: express.Request
readonly res: express.Response readonly res: express.Response
@@ -35,4 +39,4 @@ export type TRPCContextTransaction = Data.TaggedEnum<{
} }
}> }>
export const TRPCContextTransactionEnum = Data.taggedEnum<TRPCContextTransaction>() export const TRPCTransactionEnum = Data.taggedEnum<TRPCTransaction>()

View File

@@ -2,7 +2,7 @@ import type { CreateExpressContextOptions } from "@trpc/server/adapters/express"
import type { CreateWSSContextFnOptions } from "@trpc/server/adapters/ws" import type { CreateWSSContextFnOptions } from "@trpc/server/adapters/ws"
import { Context, Effect, Layer, Runtime } from "effect" import { Context, Effect, Layer, Runtime } from "effect"
import { createTRCPErrorMapper } from "./createTRCPErrorMapper" import { createTRCPErrorMapper } from "./createTRCPErrorMapper"
import { TRPCContextTransactionEnum, type TRPCContext, type TRPCContextTransaction } from "./TRPCContext" import { TRPCTransactionEnum, type TRPCContext, type TRPCTransaction } from "./TRPCContext"
export const Identifier = "@thilalib/TRPC/TRPCContextCreator" export const Identifier = "@thilalib/TRPC/TRPCContextCreator"
@@ -10,7 +10,7 @@ export const Identifier = "@thilalib/TRPC/TRPCContextCreator"
export interface TRPCContextCreator<R> extends Context.Tag<typeof Identifier, TRPCContextCreatorService<R>> {} export interface TRPCContextCreator<R> extends Context.Tag<typeof Identifier, TRPCContextCreatorService<R>> {}
export interface TRPCContextCreatorService<R> { export interface TRPCContextCreatorService<R> {
readonly createContext: (transaction: TRPCContextTransaction) => TRPCContext<R> readonly createContext: (transaction: TRPCTransaction) => TRPCContext<R>
readonly createExpressContext: (context: CreateExpressContextOptions) => TRPCContext<R> readonly createExpressContext: (context: CreateExpressContextOptions) => TRPCContext<R>
readonly createWebSocketContext: (context: CreateWSSContextFnOptions) => TRPCContext<R> readonly createWebSocketContext: (context: CreateWSSContextFnOptions) => TRPCContext<R>
} }
@@ -41,16 +41,16 @@ export const make = <R>() => {
options, options,
) )
const createContext = (transaction: TRPCContextTransaction) => ({ const createContext = (transaction: TRPCTransaction) => ({
runtime, runtime,
run, run,
fork, fork,
transaction, transaction,
}) })
const createExpressContext = (context: CreateExpressContextOptions) => createContext(TRPCContextTransactionEnum.Express(context)) const createExpressContext = (context: CreateExpressContextOptions) => createContext(TRPCTransactionEnum.Express(context))
const createWebSocketContext = (context: CreateWSSContextFnOptions) => createContext(TRPCContextTransactionEnum.WebSocket(context)) const createWebSocketContext = (context: CreateWSSContextFnOptions) => createContext(TRPCTransactionEnum.WebSocket(context))
return { createContext, createExpressContext, createWebSocketContext } return { createContext, createExpressContext, createWebSocketContext } as const
})) }))
return { TRPCContextCreator, TRPCContextCreatorLive } as const return { TRPCContextCreator, TRPCContextCreatorLive } as const

View File

@@ -1,44 +0,0 @@
import { Effect, Match } from "effect"
import type { TRPCContextTransaction } from "./TRPCContext"
import { importTRPCServer } from "./importTRPCServer"
export const ExpressOnly = importTRPCServer.pipe(Effect.map(({
experimental_standaloneMiddleware,
TRPCError,
}) => experimental_standaloneMiddleware<{
ctx: { readonly transaction: TRPCContextTransaction }
}>().create(opts =>
Match.value(opts.ctx.transaction).pipe(
Match.tag("Express", transaction =>
opts.next({ ctx: { transaction } })
),
Match.orElse(() => {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Only Express transport is supported by this procedure",
})
}),
)
)))
export const WebSocketOnly = importTRPCServer.pipe(Effect.map(({
experimental_standaloneMiddleware,
TRPCError,
}) => experimental_standaloneMiddleware<{
ctx: { readonly transaction: TRPCContextTransaction }
}>().create(opts =>
Match.value(opts.ctx.transaction).pipe(
Match.tag("WebSocket", transaction =>
opts.next({ ctx: { transaction } })
),
Match.orElse(() => {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Only WebSocket transport is supported by this procedure",
})
}),
)
)))

View File

@@ -0,0 +1,33 @@
import { Schema } from "@effect/schema"
import type { ParseOptions } from "@effect/schema/AST"
import { Effect } from "effect"
import { importTRPCServer } from "../importTRPCServer"
import type { TRPCContextRuntime } from "../TRPCContext"
export const DecodeInput = <A, I>(
schema: Schema.Schema<A, I>,
options?: ParseOptions,
) => Effect.gen(function*() {
const { experimental_standaloneMiddleware, TRPCError } = yield* importTRPCServer
const decode = (value: I) => Schema.decode(schema, options)(value).pipe(
Effect.matchEffect({
onSuccess: Effect.succeed,
onFailure: e => Effect.fail(new TRPCError({
code: "BAD_REQUEST",
message: "Could not decode input",
cause: e,
})),
})
)
return experimental_standaloneMiddleware<{
ctx: TRPCContextRuntime<any>
input: I
}>().create(
async ({ ctx, input, next }) => next({
ctx: { decodedInput: await ctx.run(decode(input)) } as const
})
)
})

View File

@@ -0,0 +1,22 @@
import { Effect, identity, Match } from "effect"
import { importTRPCServer } from "../importTRPCServer"
import type { TRPCContextTransaction } from "../TRPCContext"
export const ExpressOnly = importTRPCServer.pipe(Effect.map(({
experimental_standaloneMiddleware,
TRPCError,
}) => experimental_standaloneMiddleware<{
ctx: TRPCContextTransaction
}>().create(({ ctx, next }) => next({
ctx: Match.value(ctx.transaction).pipe(
Match.tag("Express", identity),
Match.orElse(() => {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Only Express transport is supported by this procedure",
})
}),
)
}))))

View File

@@ -0,0 +1,22 @@
import { Effect, identity, Match } from "effect"
import { importTRPCServer } from "../importTRPCServer"
import type { TRPCContextTransaction } from "../TRPCContext"
export const WebSocketOnly = importTRPCServer.pipe(Effect.map(({
experimental_standaloneMiddleware,
TRPCError,
}) => experimental_standaloneMiddleware<{
ctx: TRPCContextTransaction
}>().create(({ ctx, next }) => next({
ctx: Match.value(ctx.transaction).pipe(
Match.tag("WebSocket", identity),
Match.orElse(() => {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Only WebSocket transport is supported by this procedure",
})
}),
)
}))))

View File

@@ -0,0 +1,3 @@
export * from "./DecodeInput"
export * from "./ExpressOnly"
export * from "./WebSocketOnly"