@@ -6,6 +6,7 @@ import type Router from "@koa/router"
66import Koa , { type Middleware } from "koa"
77import KoaBody from "koa-body"
88import type { KoaBodyMiddlewareOptions } from "koa-body/lib/types"
9+ import { responseValidationFactory } from "./zod"
910
1011// from https://stackoverflow.com/questions/39494689/is-it-possible-to-restrict-number-to-a-certain-range
1112type Enumerate <
@@ -54,15 +55,29 @@ export class KoaRuntimeResponse<Type> {
5455}
5556
5657export type ResponderBuilder = {
57- [ key in `with${StatusCode } `] : < T > ( ) => KoaRuntimeResponse < T >
58+ [ key in `with${StatusCode } `] : < T , S = unknown > (
59+ s : S ,
60+ ) => ( ) => KoaRuntimeResponse < T >
5861} & {
5962 withStatus ( status : StatusCode ) : KoaRuntimeResponse < unknown >
60- withStatusCode1xx < T > ( status : StatusCode1xx ) : KoaRuntimeResponse < T >
61- withStatusCode2xx < T > ( status : StatusCode2xx ) : KoaRuntimeResponse < T >
62- withStatusCode3xx < T > ( status : StatusCode3xx ) : KoaRuntimeResponse < T >
63- withStatusCode4xx < T > ( status : StatusCode4xx ) : KoaRuntimeResponse < T >
64- withStatusCode5xx < T > ( status : StatusCode5xx ) : KoaRuntimeResponse < T >
65- withDefault < T > ( status : StatusCode ) : KoaRuntimeResponse < T >
63+ withStatusCode1xx < T , S = unknown > (
64+ s : S ,
65+ ) : ( status : StatusCode1xx ) => KoaRuntimeResponse < T >
66+ withStatusCode2xx < T , S = unknown > (
67+ s : S ,
68+ ) : ( status : StatusCode2xx ) => KoaRuntimeResponse < T >
69+ withStatusCode3xx < T , S = unknown > (
70+ s : S ,
71+ ) : ( status : StatusCode3xx ) => KoaRuntimeResponse < T >
72+ withStatusCode4xx < T , S = unknown > (
73+ s : S ,
74+ ) : ( status : StatusCode4xx ) => KoaRuntimeResponse < T >
75+ withStatusCode5xx < T , S = unknown > (
76+ s : S ,
77+ ) : ( status : StatusCode5xx ) => KoaRuntimeResponse < T >
78+ withDefault < T , S = unknown > (
79+ s : S ,
80+ ) : ( status : StatusCode ) => KoaRuntimeResponse < T >
6681}
6782
6883function isValidStatusCode ( status : unknown ) : status is StatusCode {
@@ -72,6 +87,73 @@ function isValidStatusCode(status: unknown): status is StatusCode {
7287 return ! ( status < 100 || status > 599 )
7388}
7489
90+ export const b = < T > ( fn : ( r : ResponderBuilder ) => T ) => {
91+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
92+ const responses : any [ ] = [ ]
93+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
94+ let defaultResponse : any = undefined
95+
96+ const r : ResponderBuilder = new Proxy ( { } as ResponderBuilder , {
97+ get ( _ , prop : string ) {
98+ const exactMatch = / ^ w i t h ( \d { 3 } ) $ / . exec ( prop )
99+
100+ if ( exactMatch ?. [ 1 ] ) {
101+ const status = Number ( exactMatch [ 1 ] )
102+
103+ if ( ! isValidStatusCode ( status ) ) {
104+ throw new Error ( `Status ${ status } is not a valid status code` )
105+ }
106+
107+ return < S > ( s : S ) => {
108+ responses . push ( [ status . toString ( ) , s ] )
109+
110+ return < T > ( ) => new KoaRuntimeResponse < T > ( status )
111+ }
112+ }
113+
114+ const groupMatch = / ^ w i t h S t a t u s C o d e ( [ 1 - 5 ] x x ) $ / . exec ( prop )
115+ if ( groupMatch ?. [ 1 ] ) {
116+ const range = groupMatch [ 1 ]
117+
118+ return < S > ( s : S ) => {
119+ responses . push ( [ range , s ] )
120+
121+ return < T > ( status : StatusCode ) => {
122+ const expectedHundreds = Number ( range [ 0 ] )
123+ if ( Math . floor ( status / 100 ) !== expectedHundreds ) {
124+ throw new Error (
125+ `Status ${ status } is not a valid ${ range } status code` ,
126+ )
127+ }
128+ return new KoaRuntimeResponse < T > ( status )
129+ }
130+ }
131+ }
132+
133+ if ( prop === "withDefault" ) {
134+ return < S > ( s : S ) => {
135+ defaultResponse = s
136+
137+ return < T > ( status : StatusCode ) => new KoaRuntimeResponse < T > ( status )
138+ }
139+ }
140+
141+ if ( prop === "withStatus" ) {
142+ return ( status : StatusCode ) => new KoaRuntimeResponse < unknown > ( status )
143+ }
144+
145+ throw new Error ( `Unknown responder method: ${ prop } ` )
146+ } ,
147+ } )
148+
149+ const responder = fn ( r )
150+
151+ return {
152+ responder,
153+ validator : responseValidationFactory ( responses , defaultResponse ) ,
154+ }
155+ }
156+
75157export const r : ResponderBuilder = new Proxy ( { } as ResponderBuilder , {
76158 get ( _ , prop : string ) {
77159 const exactMatch = / ^ w i t h ( \d { 3 } ) $ / . exec ( prop )
0 commit comments