feat: separate input/output types on AppType for Eden Treaty#1728
feat: separate input/output types on AppType for Eden Treaty#1728raunak-rpm wants to merge 4 commits intoelysiajs:mainfrom
Conversation
…lysiajs#1718) - Add UnwrapSchemaInput type: resolves Standard Schema to input type (pre-transform) instead of output type (post-transform) - Add UnwrapBodySchemaInput type: body variant with null support - Add UnwrapRouteInput interface: resolves route to input types for body, headers, query, params; response stays as output - Extend CreateEdenResponse with optional SchemaInput parameter and new 'input' property containing pre-transform types - Thread SchemaInput through all 11 HTTP method signatures - Export new type utilities for external consumers (Eden Treaty) - Add 11 comprehensive type-level tests with Zod transforms When no transforms are used, input === output (zero overhead). For Zod/Valibot transforms, input reflects the raw shape clients send. Fully backward compatible: existing body/query/etc remain output types.
WalkthroughAdds input-focused type utilities and propagates input generics through route/macro signatures so route types expose pre-transform (input) shapes alongside existing output types; updates public exports and adds extensive type- and runtime-tests validating input vs output separation. Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/types.ts (1)
2767-2823:⚠️ Potential issue | 🟠 MajorMacro-defined transforms still leak output types into
Route['input'].
CreateEdenResponse['input']intersectsSchemaInputwithMacroContext, butMacroContextis derived viaUnwrapRoute(output). If a macro contributes a Standard Schema with.transform(), the client-facinginput.*types will still reflect the post-transform shape.Consider introducing an input-oriented macro context (e.g.,
MacroToContextInputusingUnwrapRouteInput) and threading it intoCreateEdenResponse.💡 Possible fix sketch
-export type CreateEdenResponse< +export type CreateEdenResponse< Path extends string, Schema extends RouteSchema, MacroContext extends RouteSchema, Res extends PossibleResponse, - SchemaInput extends RouteSchema = Schema + SchemaInput extends RouteSchema = Schema, + MacroContextInput extends RouteSchema = MacroContext > = RouteSchema extends MacroContext ? { body: Schema['body'] params: IsNever<keyof Schema['params']> extends true ? ResolvePath<Path> : Schema['params'] query: Schema['query'] headers: Schema['headers'] response: Prettify<Res> input: { - body: SchemaInput['body'] + body: SchemaInput['body'] params: IsNever<keyof SchemaInput['params']> extends true ? ResolvePath<Path> : SchemaInput['params'] query: SchemaInput['query'] headers: SchemaInput['headers'] } } : { body: Prettify<Schema['body'] & MacroContext['body']> params: IsNever< keyof (Schema['params'] & MacroContext['params']) > extends true ? ResolvePath<Path> : Prettify<Schema['params'] & MacroContext['params']> query: Prettify<Schema['query'] & MacroContext['query']> headers: Prettify<Schema['headers'] & MacroContext['headers']> response: Prettify<Res> input: { - body: Prettify<SchemaInput['body'] & MacroContext['body']> + body: Prettify<SchemaInput['body'] & MacroContextInput['body']> params: IsNever< - keyof (SchemaInput['params'] & MacroContext['params']) + keyof (SchemaInput['params'] & MacroContextInput['params']) > extends true ? ResolvePath<Path> - : Prettify<SchemaInput['params'] & MacroContext['params']> - query: Prettify<SchemaInput['query'] & MacroContext['query']> - headers: Prettify<SchemaInput['headers'] & MacroContext['headers']> + : Prettify<SchemaInput['params'] & MacroContextInput['params']> + query: Prettify<SchemaInput['query'] & MacroContextInput['query']> + headers: Prettify<SchemaInput['headers'] & MacroContextInput['headers']> } }
- Fix 9 broken existing type tests (add input property to assertions) - Add 28 deep type-level edge case tests (coerce, default, optional, chained transforms, nested, arrays, unions, guards, all HTTP methods, plugins, mixed TypeBox+Zod, Eden contract, TypeBox transforms) - Add 9 runtime tests verifying transforms work correctly - TSC: 0 errors (down from 58), Tests: 1509 pass, 0 fail
commit: |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/index.ts`:
- Around line 7105-7109: The WS helper's MacroToContextInput generic is missing
the Definitions['typebox'] parameter, causing TypeBox ref resolution to differ
from HTTP helpers; update the MacroToContextInput usage near SchemaInput so it
becomes MacroToContextInput<Definitions['typebox'], Metadata['macroFn'],
Omit<Input, NonResolvableMacroKey>> (i.e., add Definitions['typebox'] as the
first generic type argument) so WS macro input types are consistent with other
HTTP method helpers.
🧹 Nitpick comments (1)
src/types.ts (1)
497-544: Consider a generic parameterized approach to reduce type duplication.There are now effectively four pairs of near-identical types (
UnwrapSchema/UnwrapSchemaInput,UnwrapBodySchema/UnwrapBodySchemaInput,UnwrapMacroSchema/UnwrapMacroSchemaInput,MacroToContext/MacroToContextInput) that differ only in whether they resolve['input']vs['output']on Standard Schema. This is ~200 lines of duplicated logic.A possible approach is to introduce a discriminator generic (e.g.,
type SchemaMode = 'input' | 'output') and unify each pair into a single parameterized type:// Sketch – single source of truth for both modes type UnwrapSchemaMode< Mode extends 'input' | 'output', Schema extends AnySchema | string | undefined, Definitions extends DefinitionBase['typebox'] = {} > = Schema extends undefined ? unknown : Schema extends TSchema ? /* ...TSchema branch (identical for both modes)... */ : Schema extends FastStandardSchemaV1Like ? NonNullable<Schema['~standard']['types']>[Mode] : /* ...string branch with [Mode]... */ // Aliases for ergonomics / backward compat export type UnwrapSchema<S, D> = UnwrapSchemaMode<'output', S, D> export type UnwrapSchemaInput<S, D> = UnwrapSchemaMode<'input', S, D>This would halve the surface area and ensure both modes stay in sync when the TSchema / string branches are updated. That said, I understand the trade-off with readability in complex conditional types, so this can be deferred.
Also applies to: 587-629, 1255-1350
Summary
Fixes #1718 — Adds separate input/output types on the AppType so Eden Treaty can differentiate between what the client sends (pre-transform) and what the handler receives (post-transform).
Problem
When using schemas with transforms (e.g.,
z.string().datetime().transform(v => new Date(v))), Elysia's AppType only exposes the output type (Date). Eden Treaty reads this type for client request payloads, but the client should send the input type (string).Currently
UnwrapSchemaalways resolves to['output']for Standard Schema and['static'](decoded) for TypeBox.CreateEdenResponse— the contract type Eden reads — only contains these output types.Solution
New Type Utilities (
src/types.ts)UnwrapSchemaInput— LikeUnwrapSchemabut resolves Standard Schema to['input']instead of['output']. TypeBox schemas resolve identically (input === output due to Elysia's cast pattern).UnwrapBodySchemaInput— Body variant ofUnwrapSchemaInputwithnullsupport for optional bodies.UnwrapRouteInput— LikeUnwrapRoutebut uses input type utilities for body, headers, query, params. Response stays as output (it's what the server sends back).Extended Eden Contract (
CreateEdenResponse)Added an optional
SchemaInputtype parameter (defaults toSchemafor backward compatibility) and a newinputproperty:Route Method Updates (
src/index.ts)All 11 HTTP method signatures (
get,post,put,patch,delete,options,all,head,connect,route,ws) now computeSchemaInputviaUnwrapRouteInputand pass it toCreateEdenResponse.Backward Compatibility
body,query,headers,paramsremain output typesinput === output(no overhead)Usage
Testing
expect-typecovering:Summary by CodeRabbit
New Features
Tests