Giter Club home page Giter Club logo

hyper's Introduction

Hyper

Type-safe, statically checked composition of HTTP servers

Getting Started | Documentation | FAQ | Examples


Hyper is an experimental middleware architecture for HTTP servers written in PureScript. Its main focus is correctness and type-safety, using type-level information to enforce correct composition and abstraction for web servers. The Hyper project is also a breeding ground for higher-level web server constructs, which tend to fall under the “framework” category.

To learn more about Hyper, check out the documentation and the Getting Started tutorial.

Build Status purescript-hyper on Pursuit Hyper in package-sets


Development Setup

Prerequisites

  • PureScript 0.13.0 or higher
  • NodeJS
  • Spago

Build

Install dependencies and build:

spago build

Run Tests

spago test

Run Examples

# general format:
spago run -p examples/<example-name>.purs -m Examples.<example-name>

# for instance to run HelloHyper:
spago run -p examples/HelloHyper.purs -m Examples.HelloHyper

Build All Examples

make examples

Building Documentation

See docs/README.md for prerequisites, setup, and how to work with the documentation.

Then, for the release build, run:

make docs

CodeScene Status

Get more details at codescene.io.

License

Mozilla Public License Version 2.0

hyper's People

Contributors

bklaric avatar garyb avatar jimmyhuco avatar jordanmartinez avatar justinwoo avatar klntsky avatar nsaunders avatar owickstrom avatar paluh avatar rightfold avatar th-awake avatar ulsa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hyper's Issues

Remodel code via Indexed Capability Design Pattern?

Due to the concerns raised in #86, I doubt this idea is desired. Still, I thought I would bring it up as a possible discussion.

The idea is to convert this codebase from using a variant of the StateT monad (i.e. Middleware) into using the indexed capability design pattern (see Exploration - Indexed Capability Design Pattern with Indexed Monad Transformers for context).

In my fork of this repo, I'm exploring what this would ultimately look like if the approach was taken, and what are its pain points would be.

Second `readBuffer` call hangs forever

It seems that second call to readBuffer hangs forever. Minimal example:

 main :: forall e. Eff (avar  AVAR, buffer  BUFFER, console :: CONSOLE, http :: HTTP | e) Unit
 main =
   let app = writeStatus statusOK
             :*> (readBody :: Middleware _ _ _ String)
             :*> (readBody :: Middleware _ _ _ String)
             :*> closeHeaders
             :*> respond "Hello, Hyper!"
   in runServer defaultOptionsWithLogging {} app

I'm not sure, but maybe body state also should be tracked in types... What do you think?

Type-safe forms

Goals:

  • Rendering forms for POST resources with application/www-form-urlencoded content type should be type-safe
    • All fields present
    • Correct input types for fields
  • Rendered forms should be customizable/extendable in some way, so that the user can add help texts, class attributes, etc

Notes:

The current working design does not enforce type-safety, it merely gives you all the input fields with correct types to assemble your form with. Thus, you could override the type attribute, omit certain fields, etc. I'm not sure if it is feasible to enforce it without having some type-level Set construct. Anyway, the first attempts will do this less safe approach, and then we can iterate.

The idea is to represent forms as such:

type PersonForm =
  InputHidden "id" Int
  :<> InputText "name"
  :<> InputNumber "age"

And then in rendering, do something like:

instance encodeHTMLNewPerson :: EncodeHTML NewPerson where
  encodeHTML _ =
    case toForms site of
      -- this requires a `ReqBody FormUrlEncoded PersonForm` in the corr routing type
      _ :<|> _ :<|> savePersonForm :<|> _ →
        savePersonForm renderForm
  where
    renderForm (idField :<> nameField :<> ageField) = do
      idField -- automatically type=hidden
      label "Age"
      nameField
      label "Age"
      ageField -- automatically type=number

And have the rendered fields automatically enclosed in a correct <form> with action and method attributes.

Branch: https://github.com/owickstrom/hyper/tree/type-safe-forms

TODO:

  • (Basic) type-level representation of forms
  • FromFormData/ToFormData type classes
  • Generate extendable markup for forms
  • ReqBody ct t for reading, parsing, and passing form data to handlers

Add hyper to package-sets

Today I've added indexed-monad to package-sets and verified that after migration to aff-4.0.0 all dependencies are met and we can add hyper to the packages index.
I will happily provide appropriate pull request to package-sets repo.

Redefine `IxMonadMiddleware` to mirror `MonadState` more

Currently, IxMonadMiddleware is defined as two functions: getConn and putConn. As a result, modifyConn requires two steps when it only needs one.

class IxMonad m <= IxMonadMiddleware m where
  getConn   i. m i i i
  putConn   i o. o  m i o Unit

modifyConn   m i o
  .  IxMonadMiddleware m
  => (i  o) -> m i o Unit
modifyConn f = getConn :>>= \c -> putConn (f c)

instance ixMonadMiddlewareMiddleware :: Monad m  IxMonadMiddleware (Middleware m) where
  getConn = Middleware $ \c -> pure (Tuple c c)
  putConn c = Middleware $ \_ -> pure (Tuple unit c)

Contrast that with MonadState, which defines a single function state and 4 derived functions: get, put, modify, and modify_.

class Monad m <= MonadState s m | m -> s where
  state :: forall a. (s -> (Tuple a s)) -> m a

-- | Get the current state.
get :: forall m s. MonadState s m => m s
get = state \s -> Tuple s s

-- | Get a value which depends on the current state.
gets :: forall s m a. MonadState s m => (s -> a) -> m a
gets f = state \s -> Tuple (f s) s

-- | Set the state.
put :: forall m s. MonadState s m => s -> m Unit
put s = state \_ -> Tuple unit s

-- | Modify the state by applying a function to the current state. The returned
-- | value is the new state value.
modify :: forall s m. MonadState s m => (s -> s) -> m s
modify f = state \s -> let s' = f s in Tuple s' s'

modify_ :: forall s m. MonadState s m => (s -> s) -> m Unit
modify_ f = state \s -> Tuple unit (f s)

If we were to make this type class mirror MonadState, we would define it like so:

class IxMonad m <= IxMonadMiddleware m where
  stateConn :: forall i o a. (i -> (Tuple a o)) -> m i o a

instance ixMonadMiddlewareMiddleware :: Monad m => IxMonadMiddleware (Middleware m) where
  stateConn :: forall i o a. (i -> (Tuple a o)) -> Middleware m i o a
  stateConn f = Middleware (pure <<< f)

getConn :: forall m i. IxMonadMiddleware m => m i i i
getConn = stateConn (\i -> Tuple i i)

putConn :: forall m i o. IxMonadMiddleware m => o -> m i o Unit
putConn o = stateConn (\_ -> Tuple unit o)

modifyConn :: forall m i o. IxMonadMiddleware m => (i -> o) -> m i o o
modifyConn f = stateConn (\i -> let o = f i in Tuple o o)

modifyConn_ :: forall m i o. IxMonadMiddleware m => (i -> o) -> m i o Unit
modifyConn_ f = stateConn (\i -> Tuple unit (f i))

Define kind `ResponseState` and annoate `res` with kind `ResponseState -> Type`

The res type in Conn is implicitly Type -> Type despite its declaration not showing that.

module Hyper.Conn where

-- | A `Conn` models the entirety of an HTTP connection, containing the fields
-- | `request`, `response`, and the extensibility point `components`.
type Conn req res components =
  { request :: req
  , response :: res -- (res :: Type -> Type)
  , components :: components
  }

This isn't immediately clear to the end-reader (unless one reads through the source code or potentially the docs). Moreover, the lack of using a kind here also allows one to use other non-ResponseState types here (e.g. res Int rather than res StatusLineOpen).

I propose we instead define a kind and more clearly indicate that. When I worked on implementing this, I found that the code is easier if the Conn type also has a type parameter for the responseState that it applies to res in its definition. In other words...

module Hyper.Conn where

-- | Defines the states of an HTTP request stream. It tracks whether or not
-- | some content has already been written to an HTTP request stream.
-- |
-- | Proper order of computations. Items marked with an asterisk indicate that
-- | transitioning back to the same state is valid:
-- | StatusLineOpen -> HeadersOpen* -> BodyOpen* -> ResponseEnded
foreign import kind ResponseState

-- | Type indicating that the status-line is ready to be
-- | sent.
foreign import data StatusLineOpen :: ResponseState

-- | Type indicating that headers are ready to be
-- | sent, i.e. the body streaming has not been started.
foreign import data HeadersOpen :: ResponseState

-- | Type indicating that headers have already been
-- | sent, and that the body is currently streaming.
foreign import data BodyOpen :: ResponseState

-- | Type indicating that headers have already been
-- | sent, and that the body stream, and thus the response,
-- | is finished.
foreign import data ResponseEnded :: ResponseState

-- | A `Conn` models the entirety of an HTTP connection, containing the fields
-- | `request`, `response`, and the extensibility point `components`.
type Conn request response components (responseState :: ResponseState) =
  { request :: request
  , response :: response responseState
  , components :: components
  }

-- now the `ResponseTransition` type alias is defined like so:
-- | A middleware transitioning from one `Response` state to another.
type ResponseStateTransition m (res :: ResponseState -> Type) (from :: ResponseState) (to :: ResponseState) =
  forall req comp.
  Middleware
  m
  (Conn req res comp from)
  (Conn req res comp to)
  Unit

Add function to "finalize" routes so routes with different signatures can be combined?

I tried doing something that seemed reasonable, but I couldn't get it to work out in the end as when it came to combining my routes they disagreed, due to using readBodyAsString for one of them, and not for the others.

I worked around it just by using readBody in a handler instead, to preserve the RequestBody type, but I was thinking maybe there's something that could be done so that when you want to combine endpoints in routing the res part could be existentially hidden, making the routes compatible.

This possibly ruins some other parts of the design, so perhaps it is impractical, as I guess a TryMiddleware router or something it might not fit together anymore? But just throwing it out there as an idea anyway. I still don't know the ins and outs of the design well enough to do much more than handwave here. 😄

Parameterize return type of toResponse

The return type of toResponse should probably be something other than String, perhaps some type parameter on the Conn, so that different servers can provide types for streaming responses.

Routing to external links

There is no support for external links yet. Also no support for static resources, but that should probably be a separate issue.

Any idea why I can't build on windows?

This is what I get on windows 10. I don't have another machine available for the moment.

these are the versions:

λ pulp --version
Pulp version 10.0.0
psc version 0.10.5 using D:\Users\adrian_cirstei\AppData\Roaming\npm\psc.CMD
λ pulp build
* Building project in D:\projects\purescript\hyper
Error 1 of 3:

  at D:\projects\purescript\hyper\src\Hyper\Authentication.purs line 10, column 8 - line 10, column 8

    Unable to parse module:
    unexpected {
    expecting indentation at column 1 or end of input


  See https://github.com/purescript/documentation/blob/master/errors/ErrorParsingModule.md for more information,
  or to contribute content related to this error.

Error 2 of 3:

  at D:\projects\purescript\hyper\src\Hyper\Node\Server.purs line 157, column 24 - line 157, column 24

    Unable to parse module:
    unexpected (
    expecting indentation at column 1 or end of input


  See https://github.com/purescript/documentation/blob/master/errors/ErrorParsingModule.md for more information,
  or to contribute content related to this error.

Error 3 of 3:

  at D:\projects\purescript\hyper\src\Hyper\Test\TestServer.purs line 55, column 34 - line 55, column 34

    Unable to parse module:
    unexpected {
    expecting indentation at column 1 or end of input


  See https://github.com/purescript/documentation/blob/master/errors/ErrorParsingModule.md for more information,
  or to contribute content related to this error.


* ERROR: Subcommand terminated with exit code 1

0.9.0 release

The list of commits to master since v0.8.0 is quite lengthy, so a new release is probably quite overdue.

I have updated the outdated portions of the docs (see #73).

Aside from that, here are the remaining tasks as I see it:

  • Migrate documentation to RTD or similar (see #71). (Not doing this yet; see below.)
  • Review commits referenced above (done) and
    • Formulate a new CHANGELOG.md entry.
    • Revise docs as needed. Nothing required here.

Anything I've missed?

[Info/Question] PureScript 0.12

Just to share the changes I had to do to run the example in the Getting Started

Notice that I had to install the following (which was not specified)

bower i purescript-indexed-monad

and my final code for the Main.purs is

module Main where

import Prelude
import Control.Monad.Indexed ((:*>))
import Effect (Effect)
import Effect.Console (log)
import Hyper.Node.Server (defaultOptionsWithLogging, runServer)
import Hyper.Response (closeHeaders, respond, writeStatus)
import Hyper.Status (statusOK)
import Node.HTTP 

main :: Effect Unit
main =
  let app = writeStatus statusOK
            :*> closeHeaders
            :*> respond "Hello, Hyper!"
  in runServer defaultOptionsWithLogging {} app

It seems that the server is running fine on my localhost port 300 and I'm getting an "Hello, Hyper!" from there...

Hyper app crashes on thrown exceptions

In Server.purs, Hyper uses a combination of launchAff and catchException to run the Aff computation. However, catchException is not guaranteed to catch the exception if the exception is thrown asynchronously, as documented here.

This, coupled with #43 makes it possible to crash a Hyper app by appending an invalid query string, like ?%%%, to a URL.

A possible fix is to replace launchAff with runAff and having the successful callback do nothing. This doesn't crash the app, but doesn't return any response either. It would be nice if the user could specify an "uh oh, an unexpected error occurred" response to return in such cases. I'm not sure how that would work though.

Ability to close server

I'm currently using hyper as part of functional test with Toppokki.

The problem being that I'm unable to close the current server after each test, so when I run a new describe the old server is still active and I get a clash.

I know that Node.HTTP has a close function here, is it something I've missed or would it be a needed feature?

Handle `Set-Cookie` attributes

Hyper's setCookie is missing Set-Cookie attributes setup.

I've implemented attribute handling on this branch.

Some notes:

  • I've added Hyper.Cookie.setCookieHeaderValue function (separate from setCookie) because I want to reuse this serializer in hyperdrive application context

  • I've added this attributes setup to Hyper.Session.saveSession: SameSite=Lax;HttpOnly, but probably this setup should be even more configurable

  • I've added this attribute setup to Hyper.Session.deleteSession: MaxAge=0

  • In theory we should allow setup of multiple Set-Cookie headers, but I've read that fortunately it is not widely adopted, so we can stick probably with current behavior

  • I'm really open to any suggestion to improve/change coding style of my chages

Should I provide PR with these changes?

Numerous compiler warnings to address

This is mostly just an annoyance, I think, but here's a placeholder issue to take care of it.

Warning 1 of 17:
  in module Hyper.Status
  at src/Hyper/Status.purs line 5, column 1 - line 5, column 26
    The import of module Data.Eq is redundant
  See https://github.com/purescript/documentation/blob/master/errors/UnusedImport.md for more information,
  or to contribute content related to this warning.
Warning 2 of 17:
  in module Hyper.Status
  at src/Hyper/Status.purs line 9, column 1 - line 9, column 28
    The import of module Data.Ord is redundant
  See https://github.com/purescript/documentation/blob/master/errors/UnusedImport.md for more information,
  or to contribute content related to this warning.
Warning 3 of 17:
  in module Hyper.Status
  at src/Hyper/Status.purs line 10, column 1 - line 10, column 30
    The import of module Data.Show is redundant
  See https://github.com/purescript/documentation/blob/master/errors/UnusedImport.md for more information,
  or to contribute content related to this warning.
Warning 4 of 17:
  in module Hyper.Form
  at src/Hyper/Form.purs line 22, column 1 - line 22, column 34
    The import of module Data.Monoid is redundant
  See https://github.com/purescript/documentation/blob/master/errors/UnusedImport.md for more information,
  or to contribute content related to this warning.
Warning 5 of 17:
  in module Hyper.Node.BasicAuth
  at src/Hyper/Node/BasicAuth.purs line 25, column 1 - line 28, column 28
    Type variable e is ambiguous, since it is unused in the polymorphic type which introduces it.
  in type declaration for decodeBase64
  See https://github.com/purescript/documentation/blob/master/errors/UnusedTypeVar.md for more information,
  or to contribute content related to this warning.
Warning 6 of 17:
  in module Hyper.Node.BasicAuth
  at src/Hyper/Node/BasicAuth.purs line 33, column 1 - line 42, column 10
    Type variable e is ambiguous, since it is unused in the polymorphic type which introduces it.
  in type declaration for withAuthentication
  See https://github.com/purescript/documentation/blob/master/errors/UnusedTypeVar.md for more information,
  or to contribute content related to this warning.
Warning 7 of 17:
  in module Hyper.Node.FileServer
  at src/Hyper/Node/FileServer.purs line 115, column 1 - line 126, column 10
    Type variable e is ambiguous, since it is unused in the polymorphic type which introduces it.
  in type declaration for serveFile
  See https://github.com/purescript/documentation/blob/master/errors/UnusedTypeVar.md for more information,
  or to contribute content related to this warning.
Warning 8 of 17:
  in module Hyper.Node.FileServer
  at src/Hyper/Node/FileServer.purs line 143, column 1 - line 160, column 10
    Type variable e is ambiguous, since it is unused in the polymorphic type which introduces it.
  in type declaration for fileServer
  See https://github.com/purescript/documentation/blob/master/errors/UnusedTypeVar.md for more information,
  or to contribute content related to this warning.
Warning 9 of 17:
  in module Hyper.Node.Server
  at src/Hyper/Node/Server.purs line 62, column 1 - line 62, column 78
    Type variable e is ambiguous, since it is unused in the polymorphic type which introduces it.
  in type declaration for writeString
  See https://github.com/purescript/documentation/blob/master/errors/UnusedTypeVar.md for more information,
  or to contribute content related to this warning.
Warning 10 of 17:
  in module Hyper.Node.Server
  at src/Hyper/Node/Server.purs line 86, column 1 - line 89, column 16
    Type variable e is ambiguous, since it is unused in the polymorphic type which introduces it.
  in type declaration for readBodyAsBuffer
  See https://github.com/purescript/documentation/blob/master/errors/UnusedTypeVar.md for more information,
  or to contribute content related to this warning.
Warning 11 of 17:
  in module Hyper.Node.Server
  at src/Hyper/Node/Server.purs line 153, column 1 - line 157, column 65
    Type variable e is ambiguous, since it is unused in the polymorphic type which introduces it.
  in type declaration for setStatus
  See https://github.com/purescript/documentation/blob/master/errors/UnusedTypeVar.md for more information,
  or to contribute content related to this warning.
Warning 12 of 17:
  in module Hyper.Node.Server
  at src/Hyper/Node/Server.purs line 162, column 1 - line 166, column 68
    Type variable e is ambiguous, since it is unused in the polymorphic type which introduces it.
  in type declaration for writeHeader'
  See https://github.com/purescript/documentation/blob/master/errors/UnusedTypeVar.md for more information,
  or to contribute content related to this warning.
Warning 13 of 17:
  in module Hyper.Node.Server
  at src/Hyper/Node/Server.purs line 178, column 1 - line 181, column 67
    Type variable e is ambiguous, since it is unused in the polymorphic type which introduces it.
  in type declaration for endResponse
  See https://github.com/purescript/documentation/blob/master/errors/UnusedTypeVar.md for more information,
  or to contribute content related to this warning.
Warning 14 of 17:
  in module Hyper.Node.Server
  at src/Hyper/Node/Server.purs line 34, column 1 - line 34, column 62
    Module Hyper.Node.Server.Options was imported as Hyper.Node.Server.Options with unspecified imports.
    As this module is being re-exported, consider using the explicit form:
      import Hyper.Node.Server.Options (Hostname(..), Options, Port(..), defaultOptions, defaultOptionsWithLogging) as Hyper.Node.Server.Options
  See https://github.com/purescript/documentation/blob/master/errors/ImplicitQualifiedImportReExport.md for more information,
  or to contribute content related to this warning.
Warning 15 of 17:
  in module Hyper.Node.Server
  at src/Hyper/Node/Server.purs line 272, column 1 - line 273, column 42
    No type declaration was provided for the top-level declaration of runServer.
    It is good practice to provide type declarations as a form of documentation.
    The inferred type of runServer was:
      forall t305 t306.
        { hostname :: Hostname
        , port :: Port
        , onListening :: Hostname -> Port -> Effect Unit
        , onRequestError :: Error -> Effect Unit
        }
        -> t306
           -> Middleware Aff
                { request :: HttpRequest
                , response :: HttpResponse StatusLineOpen
                , components :: t306
                }
                { request :: HttpRequest
                , response :: HttpResponse ResponseEnded
                , components :: t305
                }
                Unit
              -> Effect Unit
  in value declaration runServer
  See https://github.com/purescript/documentation/blob/master/errors/MissingTypeDeclaration.md for more information,
  or to contribute content related to this warning.
Warning 16 of 17:
  in module Hyper.Node.Session.InMemory
  at src/Hyper/Node/Session/InMemory.purs line 16, column 1 - line 16, column 62
    Type variable eff is ambiguous, since it is unused in the polymorphic type which introduces it.
  See https://github.com/purescript/documentation/blob/master/errors/UnusedTypeVar.md for more information,
  or to contribute content related to this warning.
Warning 17 of 17:
  in module Hyper.Node.Session.InMemory
  at src/Hyper/Node/Session/InMemory.purs line 44, column 1 - line 46, column 43
    Type variable e is ambiguous, since it is unused in the polymorphic type which introduces it.
  in type declaration for newInMemorySessionStore
  See https://github.com/purescript/documentation/blob/master/errors/UnusedTypeVar.md for more information,
  or to contribute content related to this warning.

Adapter for express js middleware

Being able to reuse existing express middleware would be really nice in hyper. As it currently is, nodejs apps using express that are transitioning to purescript wouldn't be able to switch to hyper immediately because all of their infrastructure would be bound to the express middleware. They would be forced to either use the express wrapper (not ideal) or leave it as is (even worse).

I think hyper middleware should be completely compatible with express middleware as far as I can tell. What needs to be done to get this adapter going?

Encode routed resources in the Conn type

As described in https://owickstrom.github.io/hyper/#resource-routers, routed resources are not part of the Conn type. They should be encoded as a component in the Conn, if possible. My hope is that a resource A referencing another resource B, can do so through the conn, making dependencies explicit:

\conn -> html (linkTo conn.components.routes.home (text "Home Page") conn)

I think this would also result in less need for explicit type annotations when having mutually referencing resources.

Review Travis integration

It looks like the migration to a GitHub organization broke the Travis integration. Need to review.

Polymorphic reading of request body

Should perhaps have been done in #6, but anyway:

The reading of a request body should be polymorphic in a type class, like:

class RequestBodyReader r m t where
  readBody :: Middleware m (Conn { body :: r, ... } ...) (Conn { body :: t, ... } ...)

... where r is the server-provided request body type, and t is some type required by the middleware chain. The node server could provide an instance:

instance nodeRequestBodyReader :: RequestBodyReader NodeRequestBody (Aff (avar :: AVAR ...)) String where
  readBody = ...

Without this, one cannot run a complete application, given that it parses request bodies with a server-specific middleware, together with the test server.

Parameterize the request body type

The concrete Stream type used in the BodyParser module should instead be parameterized by the server. All servers might not provide the same type of request.body. Perhaps some polymorphic way of reading the body into memory, or streaming it, needs to be provided by server (like the way servers provide a response.writer value).

fileServer doesn't handle query strings

Requesting e.g. /css/sheet.css works fine, while requesting /css/sheet.css?query=string runs the not found handler.

P.S. Would you be willing to accept PRs if I have a go on some of the issues?

Support monadic handlers in type-level router

The current type is just body in:

instance routerHandler :: ( Monad m
                          , ResponseWriter rw m wb
                          , Response wb m r
                          , IsSymbol method
                          , MimeRender body ct r
                          , HasMediaType ct
                          )
                       => Router
                          (Handler method ct body)
                          body
                          (RawHandler m req res c rw) where

Should be m body.

Support non-UTF8 requests

The String instance of readBody in the Node server assumes UTF8, it should check Content-Type header.

git clone https://github.com/purescript-hyper is too slow

it takes 5 minutes to complete on my computer

https://stackoverflow.com/a/42544963/3574379

shows

that there are pdf files in history and fonts

f2f4d706f8c0  358KiB docs/hyper.pdf
a2f47392424c  358KiB docs/hyper.pdf
218b6e7d8cda  358KiB docs/hyper.pdf
0a55271eac8e  358KiB docs/hyper.pdf
3a3026cc9570  358KiB docs/hyper.pdf
3185542167b2  358KiB docs/hyper.pdf
f93fc0431565  358KiB docs/hyper.pdf
e33b7edfd485  359KiB docs/hyper.pdf
145bb0a2c517  359KiB docs/hyper.pdf
9ec17b9f9fc5  360KiB docs/hyper.pdf
5c84ed6832f6  361KiB docs/hyper.pdf
9feb2a71aa47  361KiB docs/hyper.pdf
07e9b8e8e8e4  363KiB docs/hyper.pdf
041a3406daed  365KiB docs/hyper.pdf
4ee75d02087d  365KiB docs/hyper.pdf
70503b90110c  365KiB docs/hyper.pdf
af3b8d947871  365KiB docs/hyper.pdf
dc72873f3b2f  365KiB docs/hyper.pdf
cb659d4917d8  365KiB docs/hyper.pdf
176f2446b7c6  370KiB docs/hyper.pdf
c21794cdaf97  370KiB docs/hyper.pdf
2dd2f47c55d1  370KiB docs/hyper.pdf
89101a00bb99  370KiB docs/hyper.pdf
9bb77936abe0  371KiB docs/hyper.pdf
db5eb1692df5  371KiB docs/hyper.pdf
551672f2c926  373KiB docs/src/theme/static/fira/ttf/FiraSans-Two.ttf
5fdab23ff117  374KiB docs/hyper.pdf
76bd77239162  374KiB docs/hyper.pdf
939e582a7c6c  374KiB docs/hyper.pdf
7c401c0c2d7c  374KiB docs/hyper.pdf
aed0f89bbc92  375KiB docs/src/theme/static/fira/ttf/FiraSans-Four.ttf
2aa2ff005c4e  381KiB docs/src/theme/static/fira/ttf/FiraSans-Eight.ttf
1bfb5db65a0b  381KiB docs-src/src/theme/static/bootstrap/css/bootstrap.css.map
1af78f27b917  383KiB docs/src/theme/static/fira/ttf/FiraSans-TwoItalic.ttf
6d2b2e01b200  385KiB docs/src/theme/static/fira/ttf/FiraSans-UltraLight.ttf
24024222fe75  385KiB docs/src/theme/static/fira/ttf/FiraSans-ExtraLight.ttf
69bad634d579  387KiB docs/src/theme/static/fira/ttf/FiraSans-Thin.ttf
2ad0374b4e6b  388KiB docs/src/theme/static/fira/ttf/FiraSans-Light.ttf
84c669736d1a  389KiB docs/src/theme/static/fira/ttf/FiraSans-EightItalic.ttf
af669aaccbbd  390KiB docs/src/theme/static/fira/ttf/FiraSans-FourItalic.ttf
d108ba086bc7  391KiB docs/src/theme/static/fira/ttf/FiraSans-Hair.ttf
037bdbcef674  393KiB docs/src/theme/static/fira/ttf/FiraSans-UltraLightItalic.ttf
b199fed9309a  393KiB docs/src/theme/static/fira/ttf/FiraSans-ExtraLightItalic.ttf
f44307f599ac  393KiB docs/src/theme/static/fira/ttf/FiraSans-ThinItalic.ttf
d9fdc0e92203  394KiB docs/src/theme/static/fira/ttf/FiraSans-Regular.ttf
4b4c3a225882  395KiB docs/src/theme/static/fira/ttf/FiraSans-LightItalic.ttf
4834eede3e00  395KiB docs/src/theme/static/fira/ttf/FiraSans-Book.ttf
c454f7393ab0  395KiB docs/src/theme/static/fira/ttf/FiraSans-Medium.ttf
c5242ad55d38  401KiB docs/src/theme/static/fira/ttf/FiraSans-HairItalic.ttf
f5e49144f524  402KiB docs/src/theme/static/fira/ttf/FiraSans-Italic.ttf
4d19d52241f7  403KiB docs/src/theme/static/fira/ttf/FiraSans-BookItalic.ttf
5416cfa75881  403KiB docs/src/theme/static/fira/ttf/FiraSans-MediumItalic.ttf
821a43d7fd25  428KiB docs/src/theme/static/fira/ttf/FiraSans-SemiBold.ttf
95e16602406c  428KiB docs/src/theme/static/fira/ttf/FiraSans-Bold.ttf
48c346f654be  436KiB docs/src/theme/static/fira/ttf/FiraSans-SemiBoldItalic.ttf
1367883863e5  436KiB docs/src/theme/static/fira/ttf/FiraSans-ExtraBold.ttf
97b52ef3cd75  437KiB docs/src/theme/static/fira/ttf/FiraSans-BoldItalic.ttf
edb8bbaae1fb  446KiB docs/src/theme/static/fira/ttf/FiraSans-ExtraBoldItalic.ttf
d26f388643ec  448KiB docs/src/theme/static/fira/ttf/FiraSans-Heavy.ttf
7ae7493dd4dc  457KiB docs/src/theme/static/fira/ttf/FiraSans-HeavyItalic.ttf
ecd7974654b7  474KiB docs/src/theme/static/fira/ttf/FiraSans-Ultra.ttf
07902d5e05cc  486KiB docs/src/theme/static/fira/ttf/FiraSans-UltraItalic.ttf
0cc6f47b87d0  529KiB docs-src/src/theme/static/bootstrap/css/bootstrap.min.css.map

Invalid query strings cause exceptions

Hyper uses decodeURIComponent to parse query string. However, decodeURIComponent has an incorrect type as documented here, since it throws an exception on invalid input.

I'm unsure whether it would be better to wait for the related issue to be resolved or to use something else for decoding strings.

Support path parameters in Paths

The current Path type is:

type Path = Array String

It would be nice to have support for path parameters, like string or integer params, at least.

Just thinking out loud, and not knowing at all if it can work, but it would be even better to encode path parameters in the types so that one can not incorrectly linkTo or postTo a resource. Say that a UserProfile resource has a path like /users/:userId, where :userId is an integer, then referencing that would require an integer:

\conn -> linkTo conn.components.routes.userProfile (IntParam 15) (Text "My Profile")

Again, this is just a vision, not sure if it is feasible.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.