@@ -20,17 +20,12 @@ module PureStack.Server
20 , serveQuery
20 , serveQuery
21 , class ParsePathPiece
21 , class ParsePathPiece
22 , parsePathPiece
22 , parsePathPiece
29import Prelude
25import Prelude
30import PureStack.Route
26import PureStack.Route
32import Bun.Request (Request)
28import Bun.Request (Request)
33−import Bun.Request as Bun
34import Bun.Request as Request
29import Bun.Request as Request
35import Bun.Response as Bun.Response
30import Bun.Response as Bun.Response
36import Control.Alternative (class Plus, empty)
31import Control.Alternative (class Plus, empty)
@@ -54,19 +49,12 @@ import Foreign.Object as Object
54import Prim.Row as Row
49import Prim.Row as Row
55import Prim.RowList (class RowToList, RowList)
50import Prim.RowList (class RowToList, RowList)
56import Prim.RowList as RowList
51import Prim.RowList as RowList
57−import PureStack.Cookie (Cookie)
58−import PureStack.Cookie as Cookie
59import Record as Record
52import Record as Record
60import Record.Builder (Builder)
53import Record.Builder (Builder)
61import Record.Builder as Builder
54import Record.Builder as Builder
62import Type.Proxy (Proxy(..))
55import Type.Proxy (Proxy(..))
65− { path :: Array String
67− , query :: Map String (Array String)
57+type Route = { path :: Array String, verb :: String, query :: Map String (Array String) }
68− , originalRequest :: Bun.Request
71routeFromRequest :: Request -> Route
59routeFromRequest :: Request -> Route
72routeFromRequest req =
60routeFromRequest req =
@@ -83,81 +71,180 @@ routeFromRequest req =
83 , query: case url of
71 , query: case url of
84 Nothing -> Map.empty
72 Nothing -> Map.empty
85 Just u -> URL.query u
73 Just u -> URL.query u
86− , originalRequest: req
89type Server a = ExceptT Response Aff a
76type Server a = ExceptT Response Aff a
91newtype Nt m = Nt (forall x. m x -> Server x)
78newtype Nt m = Nt (forall x. m x -> Server x)
93−class ServeRoute :: forall k. k -> Type -> Row Type -> (Type -> Type) -> Constraint
80+class ServeRoute :: forall k. k -> Type -> (Type -> Type) -> Constraint
94−class ServeRoute route handler middlewares m | route middlewares -> handler, handler -> m where
81+class ServeRoute route handler m | route -> handler, handler -> m where
95− serveRoute :: Route -> Maybe (Nt m -> handler -> Record middlewares -> Request -> Server Response)
82+ serveRoute :: Route -> Maybe (Nt m -> handler -> Request -> Server Response)
97−instance (ServeRoute rest handler middlewares m, IsSymbol path) => ServeRoute (path / rest) handler middlewares m where
84+instance (ServeRoute rest handler m, IsSymbol path) => ServeRoute (path / rest) handler m where
98 serveRoute route@{ path } = do
85 serveRoute route@{ path } = do
99 { head: h, tail } <- Array.uncons path
86 { head: h, tail } <- Array.uncons path
100 assert $ h == (reflectSymbol @path Proxy)
87 assert $ h == (reflectSymbol @path Proxy)
101 serveRoute @rest route { path = tail }
88 serveRoute @rest route { path = tail }
103else instance
90else instance
104− ( ServeRoute rest handler middlewares m
91+ ( ServeRoute rest handler m
105 , ServeQuery row list
92 , ServeQuery row list
106 , RowToList row list
93 , RowToList row list
108− ServeRoute (Record row / rest) (Record row -> handler) middlewares m where
95+ ServeRoute (Record row / rest) (Record row -> handler) m where
109 serveRoute route@{ query } = do
96 serveRoute route@{ query } = do
110 builder <- serveQuery @row @list query
97 builder <- serveQuery @row @list query
111 serveRoute @rest route <#> (\f nt handler req -> f nt (handler $ Builder.buildFromScratch builder) req)
98 serveRoute @rest route <#> (\f nt handler req -> f nt (handler $ Builder.buildFromScratch builder) req)
114− ( ServeRoute rest handler middlewares m
116− , Row.Cons field (Bun.Request -> m x) tail middlewares
118− ServeRoute (Middleware field / rest) (x -> handler) middlewares m where
119− serveRoute route@{ originalRequest } = do
120− serveRoute @rest route <#>
121− ( \f nt@(Nt ntf) handler middlewares req -> do
122− x <- ntf $ Record.get (Proxy @field) middlewares originalRequest
123− f nt (handler x) middlewares req
126−else instance (ServeRoute rest handler middlewares m, ParsePathPiece t) => ServeRoute (t / rest) (t -> handler) middlewares m where
100+else instance (ServeRoute rest handler m, ParsePathPiece t) => ServeRoute (t / rest) (t -> handler) m where
127 serveRoute route@{ path } = do
101 serveRoute route@{ path } = do
128 { head: h, tail } <- Array.uncons path
102 { head: h, tail } <- Array.uncons path
129 t <- parsePathPiece @t h
103 t <- parsePathPiece @t h
130− serveRoute @rest route { path = tail } <#> (\f nt handler mws req -> f nt (handler t) mws req)
104+ serveRoute @rest route { path = tail } <#> (\f nt handler req -> f nt (handler t) req)
106+else instance ToResponse resp => ServeRoute (GET resp) (m resp) m where
107+ serveRoute { path, verb } = do
108+ assert $ verb == "GET"
110+ pure $ \(Nt nt) handler _req -> do
112+ pure $ toResponse resp
114+else instance ServeRoute (POST Unit Unit) (m Unit) m where
115+ serveRoute { path, verb } = do
116+ assert $ verb == "POST"
118+ pure $ \(Nt nt) handler _req -> do
121+else instance (ToResponse resp) => ServeRoute (POST Unit resp) (m resp) m where
122+ serveRoute { path, verb } = do
123+ assert $ verb == "POST"
125+ pure $ \(Nt nt) handler _req -> do
127+ pure $ toResponse resp
128+else instance (FromRequest req) => ServeRoute (POST resp Unit) (req -> m Unit) m where
129+ serveRoute { path, verb } = do
130+ assert $ verb == "POST"
132+ pure $ \(Nt nt) handler req -> do
133+ liftAff (fromRequest req) >>= case _ of
134+ Nothing -> pure $ badRequest
138+else instance (FromRequest req, ToResponse resp) => ServeRoute (POST req resp) (req -> m resp) m where
139+ serveRoute { path, verb } = do
140+ assert $ verb == "POST"
142+ pure $ \(Nt nt) handler req -> do
143+ liftAff (fromRequest req) >>= case _ of
144+ Nothing -> pure $ badRequest
146+ resp <- nt $ handler r
147+ pure $ toResponse resp
132−else instance MethodName (meth Unit Unit) => ServeRoute (meth Unit Unit) (m Unit) middlewares m where
149+else instance ServeRoute (DELETE Unit Unit) (m Unit) m where
133 serveRoute { path, verb } = do
150 serveRoute { path, verb } = do
134− assert $ verb == methodName @(meth Unit Unit)
151+ assert $ verb == "DELETE"
135 assert $ path == []
152 assert $ path == []
136− pure $ \(Nt nt) handler _middlewares _req -> do
153+ pure $ \(Nt nt) handler _req -> do
137 nt handler
154 nt handler
138 pure $ ok
155 pure $ ok
139−else instance (ToResponse resp, MethodName (meth Unit resp)) => ServeRoute (meth Unit resp) (m resp) middlewares m where
156+else instance (ToResponse resp) => ServeRoute (DELETE Unit resp) (m resp) m where
140 serveRoute { path, verb } = do
157 serveRoute { path, verb } = do
141− assert $ verb == methodName @(meth Unit resp)
158+ assert $ verb == "DELETE"
142 assert $ path == []
159 assert $ path == []
143− pure $ \(Nt nt) handler _middlewares _req -> do
160+ pure $ \(Nt nt) handler _req -> do
144 resp <- nt handler
161 resp <- nt handler
145 pure $ toResponse resp
162 pure $ toResponse resp
146−else instance (FromRequest req, MethodName (meth resp Unit)) => ServeRoute (meth resp Unit) (req -> m Unit) middlewares m where
163+else instance (FromRequest req) => ServeRoute (DELETE resp Unit) (req -> m Unit) m where
147 serveRoute { path, verb } = do
164 serveRoute { path, verb } = do
148− assert $ verb == methodName @(meth resp Unit)
165+ assert $ verb == "DELETE"
149 assert $ path == []
166 assert $ path == []
150− pure $ \(Nt nt) handler _middlewares req -> do
167+ pure $ \(Nt nt) handler req -> do
151 liftAff (fromRequest req) >>= case _ of
168 liftAff (fromRequest req) >>= case _ of
152 Nothing -> pure $ badRequest
169 Nothing -> pure $ badRequest
153 Just r -> do
170 Just r -> do
154 nt $ handler r
171 nt $ handler r
155 pure $ ok
172 pure $ ok
156−else instance (FromRequest req, ToResponse resp, MethodName (meth req resp)) => ServeRoute (meth req resp) (req -> m resp) middlewares m where
173+else instance (FromRequest req, ToResponse resp) => ServeRoute (DELETE req resp) (req -> m resp) m where
157 serveRoute { path, verb } = do
174 serveRoute { path, verb } = do
158− assert $ verb == methodName @(meth req resp)
175+ assert $ verb == "DELETE"
159 assert $ path == []
176 assert $ path == []
160− pure $ \(Nt nt) handler _middlewares req -> do
177+ pure $ \(Nt nt) handler req -> do
178+ liftAff (fromRequest req) >>= case _ of
179+ Nothing -> pure $ badRequest
181+ resp <- nt $ handler r
182+ pure $ toResponse resp
184+else instance ServeRoute (PUT Unit Unit) (m Unit) m where
185+ serveRoute { path, verb } = do
186+ assert $ verb == "PUT"
188+ pure $ \(Nt nt) handler _req -> do
191+else instance (ToResponse resp) => ServeRoute (PUT Unit resp) (m resp) m where
192+ serveRoute { path, verb } = do
193+ assert $ verb == "PUT"
195+ pure $ \(Nt nt) handler _req -> do
197+ pure $ toResponse resp
198+else instance (FromRequest req) => ServeRoute (PUT resp Unit) (req -> m Unit) m where
199+ serveRoute { path, verb } = do
200+ assert $ verb == "PUT"
202+ pure $ \(Nt nt) handler req -> do
203+ liftAff (fromRequest req) >>= case _ of
204+ Nothing -> pure $ badRequest
208+else instance (FromRequest req, ToResponse resp) => ServeRoute (PUT req resp) (req -> m resp) m where
209+ serveRoute { path, verb } = do
210+ assert $ verb == "PUT"
212+ pure $ \(Nt nt) handler req -> do
213+ liftAff (fromRequest req) >>= case _ of
214+ Nothing -> pure $ badRequest
216+ resp <- nt $ handler r
217+ pure $ toResponse resp
219+else instance ServeRoute (PATCH Unit Unit) (m Unit) m where
220+ serveRoute { path, verb } = do
221+ assert $ verb == "PATCH"
223+ pure $ \(Nt nt) handler _req -> do
226+else instance (ToResponse resp) => ServeRoute (PATCH Unit resp) (m resp) m where
227+ serveRoute { path, verb } = do
228+ assert $ verb == "PATCH"
230+ pure $ \(Nt nt) handler _req -> do
232+ pure $ toResponse resp
233+else instance (FromRequest req) => ServeRoute (PATCH resp Unit) (req -> m Unit) m where
234+ serveRoute { path, verb } = do
235+ assert $ verb == "PATCH"
237+ pure $ \(Nt nt) handler req -> do
238+ liftAff (fromRequest req) >>= case _ of
239+ Nothing -> pure $ badRequest
243+else instance (FromRequest req, ToResponse resp) => ServeRoute (PATCH req resp) (req -> m resp) m where
244+ serveRoute { path, verb } = do
245+ assert $ verb == "PATCH"
247+ pure $ \(Nt nt) handler req -> do
161 liftAff (fromRequest req) >>= case _ of
248 liftAff (fromRequest req) >>= case _ of
162 Nothing -> pure $ badRequest
249 Nothing -> pure $ badRequest
163 Just r -> do
250 Just r -> do
@@ -165,12 +252,12 @@ else instance (FromRequest req, ToResponse resp, MethodName (meth req resp)) =>
165 pure $ toResponse resp
252 pure $ toResponse resp
167else instance
254else instance
168− ( ServeAPI routes list handlers middlewares m
255+ ( ServeAPI routes list handlers m
169 , RowToList routes list
256 , RowToList routes list
171− ServeRoute routes (Record handlers) middlewares m where
258+ ServeRoute routes (Record handlers) m where
172− serveRoute route = pure $ \nt handlers middlewares req ->
259+ serveRoute route = pure $ \nt handlers req ->
173− serveAPI @routes @list nt handlers middlewares route req
260+ serveAPI @routes @list nt handlers route req
175class ServeQuery :: Row Type -> RowList Type -> Constraint
262class ServeQuery :: Row Type -> RowList Type -> Constraint
176class ServeQuery row list | list -> row where
263class ServeQuery row list | list -> row where
@@ -255,84 +342,6 @@ instance DecodeJson (Record row) => FromRequest (Record row) where
255instance FromRequest Request where
342instance FromRequest Request where
256 fromRequest req = pure $ Just req
343 fromRequest req = pure $ Just req
258−instance FromRequest Unit where
259− fromRequest _ = pure $ Just unit
265− FromRequest (Headers (Object header) req) where
266− fromRequest req = do
267− r <- fromRequest req
269− headers <- traverse fromHeader (Request.headers req)
271− pure $ Headers headers r'
274− ( RowToList headers headersList
276− , FromHeaders headers headersList
278− FromRequest (Headers (Record headers) req) where
279− fromRequest req = do
280− r <- fromRequest req
282− headers <- fromHeaders @headers @headersList (Request.headers req)
284− pure $ Headers (Builder.buildFromScratch headers) r'
286−class FromHeader t where
287− fromHeader :: String -> Maybe t
289−instance FromHeader Cookie where
290− fromHeader = Cookie.parse >>> case _ of
294−instance FromHeader String where
297−instance FromHeader Int where
298− fromHeader = Int.fromString
300−class FromHeaders :: Row Type -> RowList Type -> Constraint
301−class FromHeaders row list | list -> row where
302− fromHeaders :: Object String -> Maybe (Builder {} (Record row))
304−instance FromHeaders () RowList.Nil where
305− fromHeaders _ = Just $ identity
310− , FromHeaders tail rest
311− , Row.Cons field (Maybe t) tail row
312− , Row.Lacks field tail
314− FromHeaders row (RowList.Cons field (Maybe t) rest) where
315− fromHeaders headers = do
316− r <- fromHeaders @tail @rest headers
317− case Object.lookup (reflectSymbol @field Proxy) headers of
318− Nothing -> pure $ Builder.insert (Proxy @field) Nothing <<< r
321− pure $ Builder.insert (Proxy @field) (Just v) <<< r
326− , FromHeaders tail rest
327− , Row.Cons field t tail row
328− , Row.Lacks field tail
330− FromHeaders row (RowList.Cons field t rest) where
331− fromHeaders headers = do
332− t <- Object.lookup (reflectSymbol @field Proxy) headers >>= fromHeader
333− r <- fromHeaders @tail @rest headers
334− pure $ Builder.insert (Proxy @field) t <<< r
336class ParsePathPiece t where
345class ParsePathPiece t where
337 parsePathPiece :: String -> Maybe t
346 parsePathPiece :: String -> Maybe t
@@ -357,26 +366,26 @@ assert :: forall m. Applicative m => Plus m => Boolean -> m Unit
357assert true = pure unit
366assert true = pure unit
358assert false = empty
367assert false = empty
360−class ServeAPI :: Row Type -> RowList Type -> Row Type -> Row Type -> (Type -> Type) -> Constraint
369+class ServeAPI :: Row Type -> RowList Type -> Row Type -> (Type -> Type) -> Constraint
361−class ServeAPI row list handlers middlewares m | row -> handlers, handlers -> m where
370+class ServeAPI row list handlers m | row -> handlers, handlers -> m where
362− serveAPI :: Nt m -> Record handlers -> Record middlewares -> Route -> Request -> Server Response
371+ serveAPI :: Nt m -> Record handlers -> Route -> Request -> Server Response
364−instance ServeAPI row RowList.Nil () middlewares m where
373+instance ServeAPI row RowList.Nil () m where
365− serveAPI _ _ _ _ _ = pure notFound
374+ serveAPI _ _ _ _ = pure notFound
368− ( ServeRoute t handler middlewares m
377+ ( ServeRoute t handler m
369 , Row.Cons field t tail row
378 , Row.Cons field t tail row
370− , ServeAPI row rest restHandlers middlewares m
379+ , ServeAPI row rest restHandlers m
371 , IsSymbol field
380 , IsSymbol field
372 , Row.Cons field handler restHandlers handlers
381 , Row.Cons field handler restHandlers handlers
373 , Row.Lacks field restHandlers
382 , Row.Lacks field restHandlers
375− ServeAPI row (RowList.Cons field t rest) handlers middlewares m where
384+ ServeAPI row (RowList.Cons field t rest) handlers m where
376− serveAPI nt handlers middlewares route req =
385+ serveAPI nt handlers route req =
377 case serveRoute @t route of
386 case serveRoute @t route of
378− Nothing -> serveAPI @row @rest @restHandlers @middlewares nt (Record.delete (Proxy @field) handlers) middlewares route req
387+ Nothing -> serveAPI @row @rest @restHandlers nt (Record.delete (Proxy @field) handlers) route req
379− Just f -> f nt (Record.get (Proxy @field) handlers) middlewares req
388+ Just f -> f nt (Record.get (Proxy @field) handlers) req
381notFound :: Response
390notFound :: Response
382notFound = Response { body: EmptyResponseBody, status: 404, statusText: "Not Found", headers: Object.empty }
391notFound = Response { body: EmptyResponseBody, status: 404, statusText: "Not Found", headers: Object.empty }
@@ -393,16 +402,16 @@ ok = Response { body: EmptyResponseBody, status: 200, statusText: "OK", headers:
393-- | The first argument is used to run an arbitrary monad in handlers. To use the 'Server' monad
402-- | The first argument is used to run an arbitrary monad in handlers. To use the 'Server' monad
394-- | instead you can just pass `identity` as the first argument.
403-- | instead you can just pass `identity` as the first argument.
396− :: forall @row list handlers m middlewares
405+ :: forall @row list handlers m
397 . RowToList row list
406 . RowToList row list
398− => ServeAPI row list handlers middlewares m
407+ => ServeAPI row list handlers m
399 => (forall x. m x -> Server x)
408 => (forall x. m x -> Server x)
400− -> { handlers :: Record handlers, middlewares :: Record middlewares }
409+ -> Record handlers
401 -> Request
410 -> Request
402 -> Aff Bun.Response.Response
411 -> Aff Bun.Response.Response
403−run nt { handlers, middlewares } req = do
412+run nt handlers req = do
404 Response { body, headers, status, statusText } <-
413 Response { body, headers, status, statusText } <-
405− map (either identity identity) $ runExceptT $ serveAPI @row @list @handlers (Nt nt) handlers middlewares (routeFromRequest req) req
414+ map (either identity identity) $ runExceptT $ serveAPI @row @list @handlers (Nt nt) handlers (routeFromRequest req) req
406 let opts = { headers, status, statusText }
415 let opts = { headers, status, statusText }
407 pure $ case body of
416 pure $ case body of
408 EmptyResponseBody -> Bun.Response.string "" opts
417 EmptyResponseBody -> Bun.Response.string "" opts