From 9ab23591d659eb8f84751fe7b52f44d2a7721409 Mon Sep 17 00:00:00 2001 From: Mnigos Date: Mon, 7 Sep 2026 12:16:13 +0200 Subject: [PATCH 1/2] fix(effect): default middlewareGen type parameters so standalone middleware works on concrete procedures --- packages/effect/src/middleware.test-d.ts | 41 ++++++++++++++++++++++++ packages/effect/src/middleware.ts | 8 ++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/effect/src/middleware.test-d.ts b/packages/effect/src/middleware.test-d.ts index b57ad17e4..a2f4e713f 100644 --- a/packages/effect/src/middleware.test-d.ts +++ b/packages/effect/src/middleware.test-d.ts @@ -105,6 +105,47 @@ describe('middlewareGen', () => { void procedure }) + it('supports standalone middleware on procedures with concrete input/output', () => { + interface ServerContext extends WithEffectContext { + auth: boolean + } + + const requireAuth = middlewareGen( + function* ({ context, next }) { + expectTypeOf(context.auth).toEqualTypeOf() + yield* Service1 + + return yield* next({ context: { user: 'user' as const } }) + }, + ) + + const procedure = os + .$context() + .input(z.object({ id: z.string() })) + .output(z.string()) + .use(requireAuth) + .handler(({ context }) => { + expectTypeOf(context.user).toEqualTypeOf<'user'>() + + return 'output' + }) + + void procedure + + const decorated = os + .$context() + .middleware(requireAuth) + + const decoratedProcedure = os + .$context() + .input(z.object({ id: z.string() })) + .output(z.string()) + .use(decorated) + .handler(() => 'output') + + void decoratedProcedure + }) + it('can infer typed errors through os.middleware', () => { // Unlike `.use`, `os.middleware` types `errors` from the builder's error map // because its middleware parameter references the error map non-generically. diff --git a/packages/effect/src/middleware.ts b/packages/effect/src/middleware.ts index e0c2ec9dc..a1e14ce04 100644 --- a/packages/effect/src/middleware.ts +++ b/packages/effect/src/middleware.ts @@ -50,11 +50,11 @@ export type AnyMiddlewareGen = MiddlewareGen */ export function middlewareGen< TInContext extends Context, - TInput, - TOutput, - TErrorMap extends ErrorMap, - TYield extends Effect.Effect>, TOutContext extends Context = object, + TInput = unknown, + TOutput = any, // TOutput = any by default is important to make middleware can be used in any output by default + TErrorMap extends ErrorMap = Record, + TYield extends Effect.Effect> = Effect.Effect>, >( middleware: MiddlewareGen>, ): Middleware { From b821c82553487f9159b779bac5318b1197b57d10 Mon Sep 17 00:00:00 2001 From: Mnigos Date: Mon, 7 Sep 2026 12:59:05 +0200 Subject: [PATCH 2/2] test(effect): cover zero-type-argument middlewareGen through os.middleware --- packages/effect/src/middleware.test-d.ts | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/effect/src/middleware.test-d.ts b/packages/effect/src/middleware.test-d.ts index a2f4e713f..105d14f0e 100644 --- a/packages/effect/src/middleware.test-d.ts +++ b/packages/effect/src/middleware.test-d.ts @@ -105,6 +105,34 @@ describe('middlewareGen', () => { void procedure }) + it('infers everything without type arguments when created via os.middleware', () => { + interface ServerContext extends WithEffectContext { + auth: boolean + } + + const requireAuth = os + .$context() + .middleware(middlewareGen(function* ({ context, next }) { + expectTypeOf(context.auth).toEqualTypeOf() + yield* Service1 + + return yield* next({ context: { user: 'user' as const } }) + })) + + const procedure = os + .$context() + .input(z.object({ id: z.string() })) + .output(z.string()) + .use(requireAuth) + .handler(({ context }) => { + expectTypeOf(context.user).toEqualTypeOf<'user'>() + + return 'output' + }) + + void procedure + }) + it('supports standalone middleware on procedures with concrete input/output', () => { interface ServerContext extends WithEffectContext { auth: boolean