Update for PureScript 0.15 (#194)

* Update shell and packages

* Fix code for 0.15

* Fix tests

* Format

* Add check-pulp command

* Generate bowerfile

* Add check-pulp to CI

* Add nixfmt to formatting

* Fixup test helpers

* Take 2

* PR comments (#1)

* Nix cleanup from PR

* Use arrows functions

* Remove unnecessary step

Co-authored-by: Connor Prussin <connor@prussin.net>
This commit is contained in:
Thomas Honeyman 2022-05-04 17:02:29 -04:00 committed by GitHub
parent bc93c23f21
commit 42bf4475e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 495 additions and 436 deletions

View File

@ -5,13 +5,12 @@ on:
push:
branches:
- main
- 'v[0-9]*.[0-9]*.x'
- "v[0-9]*.[0-9]*.x"
jobs:
Test:
runs-on: ubuntu-latest
steps:
- name: Check out codebase
uses: actions/checkout@v2
@ -32,7 +31,6 @@ jobs:
Validate_Format:
runs-on: ubuntu-latest
steps:
- name: Check out codebase
uses: actions/checkout@v2
@ -46,3 +44,20 @@ jobs:
- name: Validate Format
run: nix-shell --run check-format
Validate_Pulp:
runs-on: ubuntu-latest
steps:
- name: Check out codebase
uses: actions/checkout@v2
- name: Install nix
uses: cachix/install-nix-action@v15
with:
nix_path: nixpkgs=channel:nixpkgs-unstable
- name: Install environment
run: nix-shell
- name: Verify Pulp and bower
run: nix-shell --run check-pulp

2
.gitignore vendored
View File

@ -3,3 +3,5 @@
/generated-docs
/output
/bower_components
/.psc-ide-port
/.psa-stash

View File

@ -1,4 +1,5 @@
{
"importSort": "ide",
"importWrap": "source",
"indent": 2,
"operatorsFile": null,

View File

@ -14,28 +14,27 @@
"output"
],
"dependencies": {
"purescript-aff": "^v6.0.0",
"purescript-arrays": "^v6.0.1",
"purescript-bifunctors": "^v5.0.0",
"purescript-console": "^v5.0.0",
"purescript-effect": "^v3.0.0",
"purescript-either": "^v5.0.0",
"purescript-foldable-traversable": "^v5.0.1",
"purescript-foreign-object": "^v3.0.0",
"purescript-js-uri": "https://github.com/purescript-contrib/purescript-js-uri.git#v2.0.0",
"purescript-maybe": "^v5.0.0",
"purescript-newtype": "^v4.0.0",
"purescript-node-buffer": "^v7.0.1",
"purescript-node-fs": "^v6.1.0",
"purescript-node-http": "^v6.0.0",
"purescript-node-streams": "^v5.0.0",
"purescript-options": "^v6.0.0",
"purescript-ordered-collections": "^v2.0.1",
"purescript-prelude": "^v5.0.1",
"purescript-psci-support": "^v5.0.0",
"purescript-refs": "^v5.0.0",
"purescript-strings": "^v5.0.0",
"purescript-tuples": "^v6.0.1",
"purescript-type-equality": "^v4.0.0"
"purescript-aff": "^v7.0.0",
"purescript-arrays": "^v7.0.0",
"purescript-bifunctors": "^v6.0.0",
"purescript-console": "^v6.0.0",
"purescript-effect": "^v4.0.0",
"purescript-either": "^v6.0.0",
"purescript-foldable-traversable": "^v6.0.0",
"purescript-foreign-object": "^v4.0.0",
"purescript-js-uri": "https://github.com/purescript-contrib/purescript-js-uri.git#v3.0.0",
"purescript-maybe": "^v6.0.0",
"purescript-newtype": "^v5.0.0",
"purescript-node-buffer": "^v8.0.0",
"purescript-node-fs": "^v8.0.0",
"purescript-node-http": "^v8.0.0",
"purescript-node-streams": "^v7.0.0",
"purescript-options": "^v7.0.0",
"purescript-ordered-collections": "^v3.0.0",
"purescript-prelude": "^v6.0.0",
"purescript-refs": "^v6.0.0",
"purescript-strings": "^v6.0.0",
"purescript-tuples": "^v7.0.0",
"purescript-type-equality": "^v4.0.1"
}
}

View File

@ -1,8 +1,9 @@
module Examples.AsyncResponse.Main where
import Prelude
import Effect.Console (log)
import HTTPure (ServerM, Request, ResponseM, serve, ok)
import HTTPure (Request, ResponseM, ServerM, ok, serve)
import Node.Encoding (Encoding(UTF8))
import Node.FS.Aff (readTextFile)

View File

@ -1,7 +1,4 @@
'use strict';
import crypto from 'crypto';
const crypto = require('crypto');
exports.sha256sum = function(buffer) {
return crypto.createHash('sha256').update(buffer).digest('hex');
}
export const sha256sum = buffer =>
crypto.createHash('sha256').update(buffer).digest('hex');

View File

@ -1,9 +1,10 @@
module Examples.BinaryRequest.Main where
import Prelude
import Effect.Console (log)
import HTTPure (Request, ResponseM, ServerM, ok, serve, toBuffer)
import Node.Buffer (Buffer)
import HTTPure (Request, ResponseM, ServerM, toBuffer, serve, ok)
foreign import sha256sum :: Buffer -> String

View File

@ -1,9 +1,10 @@
module Examples.BinaryResponse.Main where
import Prelude
import Effect.Console (log)
import HTTPure (Headers, Request, ResponseM, ServerM, header, ok', serve)
import Node.FS.Aff (readFile)
import HTTPure (ServerM, Request, ResponseM, Headers, serve, ok', header)
-- | The path to the file containing the response to send
filePath :: String

View File

@ -1,11 +1,12 @@
module Examples.Chunked.Main where
import Prelude
import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Effect.Console (log)
import HTTPure (ServerM, Request, ResponseM, serve, ok)
import Node.ChildProcess (stdout, spawn, defaultSpawnOptions)
import HTTPure (Request, ResponseM, ServerM, ok, serve)
import Node.ChildProcess (defaultSpawnOptions, spawn, stdout)
import Node.Stream (Readable)
-- | Run a script and return it's stdout stream

View File

@ -1,11 +1,12 @@
module Examples.CustomStack.Main where
import Prelude
import Control.Monad.Reader (class MonadAsk, ReaderT, asks, runReaderT)
import Effect.Aff (Aff)
import Effect.Aff.Class (class MonadAff)
import Effect.Console (log)
import HTTPure (Request, Response, ResponseM, ServerM, serve, ok)
import HTTPure (Request, Response, ResponseM, ServerM, ok, serve)
-- | A type to hold the environment for our ReaderT
type Env = { name :: String }

View File

@ -1,8 +1,9 @@
module Examples.Headers.Main where
import Prelude
import Effect.Console (log)
import HTTPure (ServerM, Headers, Request, ResponseM, (!@), header, serve, ok')
import HTTPure (Headers, Request, ResponseM, ServerM, header, ok', serve, (!@))
-- | The headers that will be included in every response.
responseHeaders :: Headers

View File

@ -1,8 +1,9 @@
module Examples.HelloWorld.Main where
import Prelude
import Effect.Console (log)
import HTTPure (ServerM, serve, ok)
import HTTPure (ServerM, ok, serve)
-- | Boot up the server
main :: ServerM

View File

@ -1,9 +1,10 @@
module Examples.Middleware.Main where
import Prelude
import Effect.Class (liftEffect)
import Effect.Console (log)
import HTTPure (Request, ResponseM, ServerM, serve, fullPath, header, ok, ok')
import HTTPure (Request, ResponseM, ServerM, fullPath, header, ok, ok', serve)
-- | A middleware that logs at the beginning and end of each request
loggingMiddleware ::

View File

@ -1,8 +1,9 @@
module Examples.MultiRoute.Main where
import Prelude
import Effect.Console (log)
import HTTPure (Request, ResponseM, ServerM, serve, ok, notFound)
import HTTPure (Request, ResponseM, ServerM, notFound, ok, serve)
-- | Specify the routes
router :: Request -> ResponseM

View File

@ -1,8 +1,9 @@
module Examples.PathSegments.Main where
import Prelude
import Effect.Console (log)
import HTTPure (Request, ResponseM, ServerM, (!@), serve, ok)
import HTTPure (Request, ResponseM, ServerM, ok, serve, (!@))
-- | Specify the routes
router :: Request -> ResponseM

View File

@ -1,15 +1,16 @@
module Examples.Post.Main where
import Prelude
import Effect.Console (log)
import HTTPure
( Request
( Method(Post)
, Request
, ResponseM
, ServerM
, Method(Post)
, serve
, ok
, notFound
, ok
, serve
, toString
)

View File

@ -1,8 +1,9 @@
module Examples.QueryParameters.Main where
import Prelude
import Effect.Console (log)
import HTTPure (Request, ResponseM, ServerM, (!@), (!?), serve, ok)
import HTTPure (Request, ResponseM, ServerM, ok, serve, (!?), (!@))
-- | Specify the routes
router :: Request -> ResponseM

View File

@ -1,8 +1,9 @@
module Examples.SSL.Main where
import Prelude
import Effect.Console (log)
import HTTPure (Request, ResponseM, ServerM, serveSecure, ok)
import HTTPure (Request, ResponseM, ServerM, ok, serveSecure)
-- | The path to the certificate file
cert :: String

View File

@ -1,5 +1,5 @@
let upstream =
https://github.com/purescript/package-sets/releases/download/psc-0.14.4-20211030/packages.dhall sha256:5cd7c5696feea3d3f84505d311348b9e90a76c4ce3684930a0ff29606d2d816c
https://github.com/purescript/package-sets/releases/download/psc-0.15.0-20220502/packages.dhall
sha256:38d347aeba9fe6359c208abe87a5cecf1ffb14294f11ad19664ae35c59b6e29a
in upstream

126
shell.nix
View File

@ -1,76 +1,84 @@
{ sources ? import ./sources.nix }:
let
{
sources ? import ./sources.nix,
nixpkgs ? sources.nixpkgs,
easy-purescript-nix ? sources.easy-purescript-nix,
niv ? sources.niv,
alejandra ? sources.alejandra,
}: let
niv-overlay = self: _: {
niv = self.symlinkJoin {
name = "niv";
paths = [ sources.niv ];
buildInputs = [ self.makeWrapper ];
paths = [niv];
buildInputs = [self.makeWrapper];
postBuild = ''
wrapProgram $out/bin/niv \
--add-flags "--sources-file ${toString ./sources.json}"
'';
};
};
easy-purescript-nix-overlay = pkgs: _: {
inherit (import sources.easy-purescript-nix { inherit pkgs; }) purescript purs-tidy spago;
inherit (import easy-purescript-nix {inherit pkgs;}) purescript purs-tidy spago psa pulp-16_0_0-0;
};
pkgs = import sources.nixpkgs {
alejandra-overlay = self: _: {
alejandra = (import alejandra)."${self.system}";
};
pkgs = import nixpkgs {
overlays = [
niv-overlay
easy-purescript-nix-overlay
alejandra-overlay
];
};
build = pkgs.writeShellScriptBin "build" ''
if [ "$1" == "test" ]
then
spago -x test.dhall build
else
spago build
fi
'';
check = pkgs.writeShellScriptBin "check" "check-format && check-code";
check-code = pkgs.writeShellScriptBin "check-code" "spago -x test.dhall test";
check-format = pkgs.writeShellScriptBin "check-format" "purs-tidy check src test docs";
clean = pkgs.writeShellScriptBin "clean" "rm -rf output .psci_modules .spago";
docs = pkgs.writeShellScriptBin "docs" "spago docs";
example = pkgs.writeShellScriptBin "example" ''
if [ "$1" ]
then
spago -x test.dhall run --main Examples.$1.Main
else
echo "Which example would you like to run?\n\nAvailable examples:"
ls -1 ./docs/Examples | cat -n
read -rp " > " out
if [ "$out" ]
then
$0 $(ls -1 ./docs/Examples | sed "''${out}q;d")
fi
fi
'';
format = pkgs.writeShellScriptBin "format" "purs-tidy format-in-place src test docs";
repl = pkgs.writeShellScriptBin "repl" "spago repl";
in
pkgs.mkShell {
buildInputs = [
pkgs.git
pkgs.niv
pkgs.nodePackages.bower
pkgs.nodePackages.pulp
pkgs.nodejs
pkgs.purescript
pkgs.purs-tidy
pkgs.spago
build
check
check-code
check-format
clean
docs
example
format
repl
];
shellHook = "export PATH=$PATH:$PWD/node_modules/.bin";
}
scripts = pkgs.symlinkJoin {
name = "scripts";
paths = pkgs.lib.mapAttrsToList pkgs.writeShellScriptBin {
build = "spago -x \${1:-spago}.dhall build";
check = "check-format && check-code && check-pulp";
check-code = "spago -x test.dhall test";
check-format = "check-format-purescript && check-format-nix";
check-format-nix = "alejandra --check *.nix";
check-format-purescript = "purs-tidy check src test docs";
check-pulp = "bower install && pulp build";
clean = "rm -rf output .psci_modules .spago";
example = ''
if [ "$1" ]
then
spago -x test.dhall run --main Examples.$1.Main
else
echo "Which example would you like to run?\n\nAvailable examples:"
ls -1 ./docs/Examples | cat -n
read -rp " > " out
if [ "$out" ]
then
$0 $(ls -1 ./docs/Examples | sed "''${out}q;d")
fi
fi
'';
format = "format-purescript && format-nix";
format-nix = "alejandra *.nix";
format-purescript = "purs-tidy format-in-place src test docs";
generate-bower = "spago bump-version patch --no-dry-run";
generate-docs = "spago docs";
repl = "spago repl";
};
};
in
pkgs.mkShell {
buildInputs = [
pkgs.alejandra
pkgs.git
pkgs.niv
pkgs.nodePackages.bower
pkgs.nodejs-16_x
pkgs.psa
pkgs.pulp-16_0_0-0
pkgs.purescript
pkgs.purs-tidy
pkgs.spago
scripts
];
}

View File

@ -1,26 +1,38 @@
{
"alejandra": {
"branch": "main",
"description": "The Uncompromising Nix Code Formatter",
"homepage": "https://kamadorueda.github.io/alejandra/",
"owner": "kamadorueda",
"repo": "alejandra",
"rev": "00670576da082d85a51a53f58474b627ed7a5e21",
"sha256": "0mnf9yfbz58m9k6x5db808a8byp94yzgmmarz3zabi4xvsdihl8q",
"type": "tarball",
"url": "https://github.com/kamadorueda/alejandra/archive/00670576da082d85a51a53f58474b627ed7a5e21.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"easy-purescript-nix": {
"branch": "master",
"description": "Easy PureScript (and other tools) with Nix",
"homepage": "",
"owner": "justinwoo",
"repo": "easy-purescript-nix",
"rev": "82f901ce0a2d86327e2d65993a75c2ea74f229f2",
"sha256": "0qsq8bj76y3bxdl2iphknjib139z0jw75xlaih7viv9kvfm9b1lx",
"rev": "0ad5775c1e80cdd952527db2da969982e39ff592",
"sha256": "0x53ads5v8zqsk4r1mfpzf5913byifdpv5shnvxpgw634ifyj1kg",
"type": "tarball",
"url": "https://github.com/justinwoo/easy-purescript-nix/archive/82f901ce0a2d86327e2d65993a75c2ea74f229f2.tar.gz",
"url": "https://github.com/justinwoo/easy-purescript-nix/archive/0ad5775c1e80cdd952527db2da969982e39ff592.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"nixpkgs": {
"branch": "release-20.09",
"branch": "master",
"description": "A read-only mirror of NixOS/nixpkgs tracking the released channels. Send issues and PRs to",
"homepage": "https://github.com/NixOS/nixpkgs",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f7949198dcff52265b322ca8abf7450610e7e49e",
"sha256": "1q7538dj47yp9azwk3bl193d0bwmsvm53rxwww4wr9qk6qljd2r7",
"rev": "af911e8452bd05d40674bf603332f37480ceb03d",
"sha256": "1wxx4zdvqxfslqvx17jz1blndybx5jkqsp5rb5qyma1y59jsbpy3",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/f7949198dcff52265b322ca8abf7450610e7e49e.tar.gz",
"url": "https://github.com/NixOS/nixpkgs/archive/af911e8452bd05d40674bf603332f37480ceb03d.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
}
}

View File

@ -1,12 +1,11 @@
let
nivSrc = fetchTarball {
url = "https://github.com/nmattia/niv/tarball/4f038fc598d5494f5d16302270edc61ab2898c79";
sha256 = "1dwfkd942wisccpsv0kf47abl0n17d9v4zasv4bm8lvy1dhv82ia";
url = "https://github.com/nmattia/niv/tarball/df49d53b71ad5b6b5847b32e5254924d60703c46";
sha256 = "1j5p8mi1wi3pdcq0lfb881p97i232si07nb605dl92cjwnira88c";
};
sources = import "${nivSrc}/nix/sources.nix" {
sourcesFile = ./sources.json;
};
niv = import nivSrc {};
in
niv // sources
niv // sources

View File

@ -18,7 +18,6 @@
, "options"
, "ordered-collections"
, "prelude"
, "psci-support"
, "refs"
, "strings"
, "tuples"

View File

@ -13,7 +13,7 @@ module HTTPure
import HTTPure.Body (toBuffer, toStream, toString)
import HTTPure.Headers (Headers, empty, header, headers)
import HTTPure.Lookup (at, (!@), has, (!?), lookup, (!!))
import HTTPure.Lookup (at, has, lookup, (!!), (!?), (!@))
import HTTPure.Method (Method(..))
import HTTPure.Path (Path)
import HTTPure.Query (Query)
@ -21,135 +21,135 @@ import HTTPure.Request (Request, fullPath)
import HTTPure.Response
( Response
, ResponseM
, response
, response'
, emptyResponse
, emptyResponse'
-- 1xx
, continue
, continue'
, switchingProtocols
, switchingProtocols'
, processing
, processing'
-- 2xx
, ok
, ok'
, created
, created'
, accepted
, accepted'
, nonAuthoritativeInformation
, nonAuthoritativeInformation'
, noContent
, noContent'
, resetContent
, resetContent'
, partialContent
, partialContent'
, multiStatus
, multiStatus'
, alreadyReported
, alreadyReported'
, iMUsed
, iMUsed'
-- 3xx
, multipleChoices
, multipleChoices'
, movedPermanently
, movedPermanently'
, found
, found'
, seeOther
, seeOther'
, notModified
, notModified'
, useProxy
, useProxy'
, temporaryRedirect
, temporaryRedirect'
, permanentRedirect
, permanentRedirect'
-- 4xx
, badRequest
, badRequest'
, unauthorized
, unauthorized'
, paymentRequired
, paymentRequired'
, forbidden
, forbidden'
, notFound
, notFound'
, methodNotAllowed
, methodNotAllowed'
, notAcceptable
, notAcceptable'
, proxyAuthenticationRequired
, proxyAuthenticationRequired'
, requestTimeout
, requestTimeout'
, conflict
, conflict'
, gone
, gone'
, lengthRequired
, lengthRequired'
, preconditionFailed
, preconditionFailed'
, payloadTooLarge
, payloadTooLarge'
, uRITooLong
, uRITooLong'
, unsupportedMediaType
, unsupportedMediaType'
, rangeNotSatisfiable
, rangeNotSatisfiable'
, expectationFailed
, expectationFailed'
, imATeapot
, imATeapot'
, misdirectedRequest
, misdirectedRequest'
, unprocessableEntity
, unprocessableEntity'
, locked
, locked'
, failedDependency
, failedDependency'
, upgradeRequired
, upgradeRequired'
, preconditionRequired
, preconditionRequired'
, tooManyRequests
, tooManyRequests'
, requestHeaderFieldsTooLarge
, requestHeaderFieldsTooLarge'
, unavailableForLegalReasons
, unavailableForLegalReasons'
-- 5xx
, internalServerError
, internalServerError'
, notImplemented
, notImplemented'
-- 1xx
, badGateway
, badGateway'
, serviceUnavailable
, serviceUnavailable'
, badRequest
, badRequest'
, conflict
, conflict'
-- 2xx
, continue
, continue'
, created
, created'
, emptyResponse
, emptyResponse'
, expectationFailed
, expectationFailed'
, failedDependency
, failedDependency'
, forbidden
, forbidden'
, found
, found'
, gatewayTimeout
, gatewayTimeout'
, gone
, gone'
, hTTPVersionNotSupported
, hTTPVersionNotSupported'
, variantAlsoNegotiates
, variantAlsoNegotiates'
-- 3xx
, iMUsed
, iMUsed'
, imATeapot
, imATeapot'
, insufficientStorage
, insufficientStorage'
, internalServerError
, internalServerError'
, lengthRequired
, lengthRequired'
, locked
, locked'
, loopDetected
, loopDetected'
, notExtended
, notExtended'
, methodNotAllowed
, methodNotAllowed'
-- 4xx
, misdirectedRequest
, misdirectedRequest'
, movedPermanently
, movedPermanently'
, multiStatus
, multiStatus'
, multipleChoices
, multipleChoices'
, networkAuthenticationRequired
, networkAuthenticationRequired'
, noContent
, noContent'
, nonAuthoritativeInformation
, nonAuthoritativeInformation'
, notAcceptable
, notAcceptable'
, notExtended
, notExtended'
, notFound
, notFound'
, notImplemented
, notImplemented'
, notModified
, notModified'
, ok
, ok'
, partialContent
, partialContent'
, payloadTooLarge
, payloadTooLarge'
, paymentRequired
, paymentRequired'
, permanentRedirect
, permanentRedirect'
, preconditionFailed
, preconditionFailed'
, preconditionRequired
, preconditionRequired'
, processing
, processing'
, proxyAuthenticationRequired
, proxyAuthenticationRequired'
, rangeNotSatisfiable
, rangeNotSatisfiable'
, requestHeaderFieldsTooLarge
, requestHeaderFieldsTooLarge'
, requestTimeout
, requestTimeout'
, resetContent
, resetContent'
, response
, response'
, seeOther
, seeOther'
, serviceUnavailable
, serviceUnavailable'
-- 5xx
, switchingProtocols
, switchingProtocols'
, temporaryRedirect
, temporaryRedirect'
, tooManyRequests
, tooManyRequests'
, uRITooLong
, uRITooLong'
, unauthorized
, unauthorized'
, unavailableForLegalReasons
, unavailableForLegalReasons'
, unprocessableEntity
, unprocessableEntity'
, unsupportedMediaType
, unsupportedMediaType'
, upgradeRequired
, upgradeRequired'
, useProxy
, useProxy'
, variantAlsoNegotiates
, variantAlsoNegotiates'
)
import HTTPure.Server
( ServerM

View File

@ -10,19 +10,20 @@ module HTTPure.Body
) where
import Prelude
import Data.Either (Either(Right))
import Data.Maybe (Maybe(Just, Nothing))
import Effect (Effect)
import Effect.Aff (Aff, makeAff, nonCanceler)
import Effect.Class (liftEffect)
import Effect.Ref (Ref)
import Effect.Ref (read, modify, new, write) as Ref
import Effect.Ref (modify, new, read, write) as Ref
import HTTPure.Headers (Headers, header)
import Node.Buffer (Buffer, concat, fromString, size)
import Node.Buffer (toString) as Buffer
import Node.Encoding (Encoding(UTF8))
import Node.HTTP (Request, Response, requestAsStream, responseAsStream)
import Node.Stream (Stream, Readable, onData, onEnd, writeString, pipe, end)
import Node.Stream (Readable, Stream, end, onData, onEnd, pipe, writeString)
import Node.Stream (write) as Stream
import Type.Equality (class TypeEquals, to)
@ -117,7 +118,7 @@ instance bodyString :: Body String where
defaultHeaders buf
write body response = makeAff \done -> do
let stream = responseAsStream response
void $ writeString stream UTF8 body $ end stream $ done $ Right unit
void $ writeString stream UTF8 body $ const $ end stream $ const $ done $ Right unit
pure nonCanceler
-- | The instance for `Buffer` is trivial--we add a `Content-Length` header
@ -127,7 +128,7 @@ instance bodyBuffer :: Body Buffer where
defaultHeaders buf = header "Content-Length" <$> show <$> size buf
write body response = makeAff \done -> do
let stream = responseAsStream response
void $ Stream.write stream body $ end stream $ done $ Right unit
void $ Stream.write stream body $ const $ end stream $ const $ done $ Right unit
pure nonCanceler
-- | This instance can be used to send chunked data. Here, we add a

View File

@ -8,18 +8,19 @@ module HTTPure.Headers
) where
import Prelude
import Effect (Effect)
import Foreign.Object (fold)
import Data.Foldable (foldl)
import Data.FoldableWithIndex (foldMapWithIndex)
import Data.Map (Map, insert, singleton, union)
import Data.Map (empty) as Map
import Data.Map (Map, singleton, union, insert)
import Data.Newtype (class Newtype, unwrap)
import Data.String.CaseInsensitive (CaseInsensitiveString(CaseInsensitiveString))
import Data.TraversableWithIndex (traverseWithIndex)
import Data.Tuple (Tuple(Tuple))
import Node.HTTP (Request, Response, requestHeaders, setHeader)
import Effect (Effect)
import Foreign.Object (fold)
import HTTPure.Lookup (class Lookup, (!!))
import Node.HTTP (Request, Response, requestHeaders, setHeader)
-- | The `Headers` type is just sugar for a `Object` of `Strings`
-- | that represents the set of headers in an HTTP request or response.

View File

@ -9,6 +9,7 @@ module HTTPure.Lookup
) where
import Prelude
import Data.Array (index)
import Data.Map (Map)
import Data.Map (lookup) as Map

View File

@ -4,6 +4,7 @@ module HTTPure.Method
) where
import Prelude
import Node.HTTP (Request, requestMethod)
-- | These are the HTTP methods that HTTPure understands.

View File

@ -4,11 +4,12 @@ module HTTPure.Path
) where
import Prelude
import Data.Array (filter, head)
import Data.Maybe (fromMaybe)
import Data.String (Pattern(Pattern), split)
import Node.HTTP (Request, requestURL)
import HTTPure.Utils (urlDecode)
import Node.HTTP (Request, requestURL)
-- | The `Path` type is just sugar for an `Array` of `String` segments that are
-- | sent in a request and indicates the path of the resource being requested.

View File

@ -4,14 +4,15 @@ module HTTPure.Query
) where
import Prelude
import Data.Array (filter, head, tail)
import Data.Bifunctor (bimap)
import Data.Maybe (fromMaybe)
import Data.String (Pattern(Pattern), split, joinWith)
import Data.String (Pattern(Pattern), joinWith, split)
import Data.Tuple (Tuple(Tuple))
import Foreign.Object (Object, fromFoldable)
import Node.HTTP (Request, requestURL)
import HTTPure.Utils (replacePlus, urlDecode)
import Node.HTTP (Request, requestURL)
-- | The `Query` type is a `Object` of `Strings`, with one entry per query
-- | parameter in the request. For any query parameters that don't have values

View File

@ -5,6 +5,7 @@ module HTTPure.Request
) where
import Prelude
import Data.String (joinWith)
import Effect.Aff (Aff)
import Effect.Class (liftEffect)

View File

@ -134,77 +134,78 @@ module HTTPure.Response
) where
import Prelude
import Effect.Aff (Aff)
import Effect.Aff.Class (class MonadAff, liftAff)
import Effect.Class (class MonadEffect, liftEffect)
import Node.HTTP (Response) as HTTP
import HTTPure.Body (class Body, defaultHeaders, write)
import HTTPure.Headers (Headers, empty)
import HTTPure.Headers (write) as Headers
import HTTPure.Status (Status)
import HTTPure.Status
( write
, continue
, switchingProtocols
, processing
, ok
, created
, accepted
, nonAuthoritativeInformation
, noContent
, resetContent
, partialContent
, multiStatus
( accepted
, alreadyReported
, iMUsed
, multipleChoices
, movedPermanently
, found
, seeOther
, notModified
, useProxy
, temporaryRedirect
, permanentRedirect
, badRequest
, unauthorized
, paymentRequired
, forbidden
, notFound
, methodNotAllowed
, notAcceptable
, proxyAuthenticationRequired
, requestTimeout
, conflict
, gone
, lengthRequired
, preconditionFailed
, payloadTooLarge
, uRITooLong
, unsupportedMediaType
, rangeNotSatisfiable
, expectationFailed
, imATeapot
, misdirectedRequest
, unprocessableEntity
, locked
, failedDependency
, upgradeRequired
, preconditionRequired
, tooManyRequests
, requestHeaderFieldsTooLarge
, unavailableForLegalReasons
, internalServerError
, notImplemented
, badGateway
, serviceUnavailable
, badRequest
, conflict
, continue
, created
, expectationFailed
, failedDependency
, forbidden
, found
, gatewayTimeout
, gone
, hTTPVersionNotSupported
, variantAlsoNegotiates
, iMUsed
, imATeapot
, insufficientStorage
, internalServerError
, lengthRequired
, locked
, loopDetected
, notExtended
, methodNotAllowed
, misdirectedRequest
, movedPermanently
, multiStatus
, multipleChoices
, networkAuthenticationRequired
, noContent
, nonAuthoritativeInformation
, notAcceptable
, notExtended
, notFound
, notImplemented
, notModified
, ok
, partialContent
, payloadTooLarge
, paymentRequired
, permanentRedirect
, preconditionFailed
, preconditionRequired
, processing
, proxyAuthenticationRequired
, rangeNotSatisfiable
, requestHeaderFieldsTooLarge
, requestTimeout
, resetContent
, seeOther
, serviceUnavailable
, switchingProtocols
, temporaryRedirect
, tooManyRequests
, uRITooLong
, unauthorized
, unavailableForLegalReasons
, unprocessableEntity
, unsupportedMediaType
, upgradeRequired
, useProxy
, variantAlsoNegotiates
, write
) as Status
import Node.HTTP (Response) as HTTP
-- | The `ResponseM` type simply conveniently wraps up an HTTPure monad that
-- | returns a response. This type is the return type of all router/route

View File

@ -7,20 +7,21 @@ module HTTPure.Server
) where
import Prelude
import Data.Maybe (Maybe(Nothing))
import Data.Options (Options, (:=))
import Effect (Effect)
import Effect.Aff (catchError, runAff, message)
import Effect.Aff (catchError, message, runAff)
import Effect.Class (liftEffect)
import Effect.Console (error)
import Data.Maybe (Maybe(Nothing))
import Data.Options ((:=), Options)
import Node.Encoding (Encoding(UTF8))
import Node.FS.Sync (readTextFile)
import Node.HTTP (Request, Response, createServer) as HTTP
import Node.HTTP (ListenOptions, listen, close)
import Node.HTTP.Secure (createServer) as HTTPS
import Node.HTTP.Secure (SSLOptions, key, keyString, cert, certString)
import HTTPure.Request (Request, fromHTTPRequest)
import HTTPure.Response (ResponseM, internalServerError, send)
import Node.Encoding (Encoding(UTF8))
import Node.FS.Sync (readTextFile)
import Node.HTTP (ListenOptions, close, listen)
import Node.HTTP (Request, Response, createServer) as HTTP
import Node.HTTP.Secure (SSLOptions, cert, certString, key, keyString)
import Node.HTTP.Secure (createServer) as HTTPS
-- | The `ServerM` is just an `Effect` containing a callback to close the
-- | server. This type is the return type of the HTTPure serve and related

View File

@ -69,6 +69,7 @@ module HTTPure.Status
) where
import Prelude
import Effect (Effect)
import Node.HTTP (Response, setStatusCode)

View File

@ -5,9 +5,10 @@ module HTTPure.Utils
) where
import Prelude
import Data.Maybe (fromMaybe)
import Data.String (Pattern(Pattern), Replacement(Replacement), replaceAll)
import JSURI (encodeURIComponent, decodeURIComponent) as JSURI
import JSURI (decodeURIComponent, encodeURIComponent) as JSURI
encodeURIComponent :: String -> String
encodeURIComponent s = fromMaybe s $ JSURI.encodeURIComponent s

View File

@ -4,6 +4,7 @@ module HTTPure.Version
) where
import Prelude
import Node.HTTP (Request, httpVersion)
-- | These are the HTTP versions that HTTPure understands. There are five

View File

@ -12,7 +12,7 @@ import Node.Buffer (Buffer, fromString)
import Node.Buffer (toString) as Buffer
import Node.Encoding (Encoding(UTF8))
import Node.Stream (readString)
import Test.HTTPure.TestHelpers (Test, (?=), mockRequest, mockResponse, getResponseBody, stringToStream)
import Test.HTTPure.TestHelpers (Test, getResponseBody, mockRequest, mockResponse, stringToStream, (?=))
import Test.Spec (describe, it)
mockRequestBody :: String -> Aff RequestBody

View File

@ -1,14 +1,15 @@
module Test.HTTPure.HeadersSpec where
import Prelude
import Effect.Class (liftEffect)
import Data.Maybe (Maybe(Nothing, Just))
import Data.Tuple (Tuple(Tuple))
import Test.Spec (describe, it)
import HTTPure.Headers (header, headers, empty, read, write)
import Effect.Class (liftEffect)
import HTTPure.Headers (empty, header, headers, read, write)
import HTTPure.Lookup ((!!))
import Test.HTTPure.TestHelpers as TestHelpers
import Test.HTTPure.TestHelpers ((?=))
import Test.HTTPure.TestHelpers as TestHelpers
import Test.Spec (describe, it)
lookupSpec :: TestHelpers.Test
lookupSpec =

View File

@ -1,21 +1,8 @@
module Test.HTTPure.IntegrationSpec where
import Prelude
import Effect.Class (liftEffect)
import Foreign.Object (singleton, empty)
import Node.Buffer (toArray)
import Node.FS.Aff (readFile)
import Test.Spec (describe, it)
import Test.HTTPure.TestHelpers
( Test
, (?=)
, get
, get'
, getBinary
, getHeader
, post
, postBinary
)
import Examples.AsyncResponse.Main as AsyncResponse
import Examples.BinaryRequest.Main as BinaryRequest
import Examples.BinaryResponse.Main as BinaryResponse
@ -29,6 +16,20 @@ import Examples.PathSegments.Main as PathSegments
import Examples.Post.Main as Post
import Examples.QueryParameters.Main as QueryParameters
import Examples.SSL.Main as SSL
import Foreign.Object (empty, singleton)
import Node.Buffer (toArray)
import Node.FS.Aff (readFile)
import Test.HTTPure.TestHelpers
( Test
, get
, get'
, getBinary
, getHeader
, post
, postBinary
, (?=)
)
import Test.Spec (describe, it)
asyncResponseSpec :: Test
asyncResponseSpec =

View File

@ -1,11 +1,12 @@
module Test.HTTPure.LookupSpec where
import Prelude
import Data.Maybe (Maybe(Nothing, Just))
import Foreign.Object (singleton)
import Test.Spec (describe, it)
import HTTPure.Lookup ((!!), (!@), (!?))
import HTTPure.Lookup ((!!), (!?), (!@))
import Test.HTTPure.TestHelpers (Test, (?=))
import Test.Spec (describe, it)
atSpec :: Test
atSpec =

View File

@ -1,12 +1,13 @@
module Test.HTTPure.MethodSpec where
import Prelude
import Test.Spec (describe, it)
import HTTPure.Method
( Method(Get, Post, Put, Delete, Head, Connect, Options, Trace, Patch)
, read
)
import Test.HTTPure.TestHelpers (Test, (?=), mockRequest)
import Test.HTTPure.TestHelpers (Test, mockRequest, (?=))
import Test.Spec (describe, it)
showSpec :: Test
showSpec =

View File

@ -1,9 +1,10 @@
module Test.HTTPure.PathSpec where
import Prelude
import Test.Spec (describe, it)
import HTTPure.Path (read)
import Test.HTTPure.TestHelpers (Test, (?=), mockRequest)
import Test.HTTPure.TestHelpers (Test, mockRequest, (?=))
import Test.Spec (describe, it)
readSpec :: Test
readSpec =

View File

@ -1,11 +1,12 @@
module Test.HTTPure.QuerySpec where
import Prelude
import Data.Tuple (Tuple(Tuple))
import Foreign.Object (empty, singleton, fromFoldable)
import Test.Spec (describe, it)
import Foreign.Object (empty, fromFoldable, singleton)
import HTTPure.Query (read)
import Test.HTTPure.TestHelpers (Test, (?=), mockRequest)
import Test.HTTPure.TestHelpers (Test, mockRequest, (?=))
import Test.Spec (describe, it)
readSpec :: Test
readSpec =

View File

@ -1,15 +1,16 @@
module Test.HTTPure.RequestSpec where
import Prelude
import Data.Tuple (Tuple(Tuple))
import Foreign.Object (singleton)
import Test.Spec (describe, it)
import HTTPure.Body (toString)
import HTTPure.Headers (headers)
import HTTPure.Method (Method(Post))
import HTTPure.Request (fromHTTPRequest, fullPath)
import HTTPure.Version (Version(HTTP1_1))
import Test.HTTPure.TestHelpers (Test, (?=), mockRequest)
import Test.HTTPure.TestHelpers (Test, mockRequest, (?=))
import Test.Spec (describe, it)
fromHTTPRequestSpec :: Test
fromHTTPRequestSpec =

View File

@ -1,24 +1,25 @@
module Test.HTTPure.ResponseSpec where
import Prelude
import Data.Either (Either(Right))
import Effect.Aff (makeAff, nonCanceler)
import Effect.Class (liftEffect)
import Node.Encoding (Encoding(UTF8))
import Node.HTTP (responseAsStream)
import Node.Stream (writeString, end)
import Test.Spec (describe, it)
import HTTPure.Body (defaultHeaders)
import HTTPure.Headers (header)
import HTTPure.Response (send, response, response', emptyResponse, emptyResponse')
import HTTPure.Response (emptyResponse, emptyResponse', response, response', send)
import Node.Encoding (Encoding(UTF8))
import Node.HTTP (responseAsStream)
import Node.Stream (end, writeString)
import Test.HTTPure.TestHelpers
( Test
, (?=)
, mockResponse
, getResponseBody
, getResponseHeader
, getResponseStatus
, getResponseBody
, mockResponse
, (?=)
)
import Test.Spec (describe, it)
sendSpec :: Test
sendSpec =
@ -30,7 +31,7 @@ sendSpec =
, writeBody:
\response -> makeAff \done -> do
stream <- pure $ responseAsStream response
void $ writeString stream UTF8 "test" $ end stream $ done $ Right unit
void $ writeString stream UTF8 "test" $ const $ end stream $ const $ done $ Right unit
pure nonCanceler
}
it "writes the headers" do

View File

@ -1,22 +1,23 @@
module Test.HTTPure.ServerSpec where
import Prelude
import Effect.Class (liftEffect)
import Effect.Exception (error)
import Control.Monad.Except (throwError)
import Data.Maybe (Maybe(Nothing))
import Data.Options ((:=))
import Data.String (joinWith)
import Effect.Class (liftEffect)
import Effect.Exception (error)
import Foreign.Object (empty)
import Node.Encoding (Encoding(UTF8))
import Node.HTTP.Secure (key, keyString, cert, certString)
import Node.FS.Sync (readTextFile)
import Test.Spec (describe, it)
import Test.Spec.Assertions (expectError)
import HTTPure.Request (Request)
import HTTPure.Response (ResponseM, ok)
import HTTPure.Server (serve, serve', serveSecure, serveSecure')
import Test.HTTPure.TestHelpers (Test, (?=), get, get', getStatus)
import Node.Encoding (Encoding(UTF8))
import Node.FS.Sync (readTextFile)
import Node.HTTP.Secure (cert, certString, key, keyString)
import Test.HTTPure.TestHelpers (Test, get, get', getStatus, (?=))
import Test.Spec (describe, it)
import Test.Spec.Assertions (expectError)
mockRouter :: Request -> ResponseM
mockRouter { path } = ok $ "/" <> joinWith "/" path

View File

@ -1,10 +1,11 @@
module Test.HTTPure.StatusSpec where
import Prelude
import Effect.Class (liftEffect)
import Test.Spec (describe, it)
import HTTPure.Status (write)
import Test.HTTPure.TestHelpers (Test, (?=), mockResponse, getResponseStatus)
import Test.HTTPure.TestHelpers (Test, getResponseStatus, mockResponse, (?=))
import Test.Spec (describe, it)
writeSpec :: Test
writeSpec =

View File

@ -1,62 +1,50 @@
"use strict";
import { Readable } from "stream";
exports.mockRequestImpl = function(httpVersion) {
return function(method) {
return function(url) {
return function(body) {
return function(headers) {
return function() {
var stream = new require('stream').Readable({
read: function(size) {
this.push(body);
this.push(null);
}
});
stream.method = method;
stream.url = url;
stream.headers = headers;
stream.httpVersion = httpVersion;
export const mockRequestImpl = httpVersion => method => url => body => headers => () => {
const stream = new Readable({
read: function (size) {
this.push(body);
this.push(null);
}
});
stream.method = method;
stream.url = url;
stream.headers = headers;
stream.httpVersion = httpVersion;
return stream;
};
};
};
};
};
return stream;
};
exports.mockResponse = function() {
return {
export const mockResponse = () => ({
body: "",
headers: {},
write: function(str, encoding, callback) {
this.body = this.body + str;
if (callback) {
callback();
}
write: function (str, encoding, callback) {
this.body = this.body + str;
if (callback) {
callback();
}
},
end: function(str, encoding, callback) {
if (callback) {
callback();
}
end: function (str, encoding, callback) {
if (callback) {
callback();
}
},
on: function() { },
once: function() { },
emit: function() { },
on: function () {},
once: function () {},
emit: function () {},
setHeader: function(header, val) {
this.headers[header] = val;
}
};
};
setHeader: function (header, val) {
this.headers[header] = val;
},
});
exports.stringToStream = function (str) {
var stream = new require('stream').Readable();
stream._read = function () {};
stream.push(str);
stream.push(null);
return stream;
export const stringToStream = str => {
const stream = new Readable();
stream._read = function () {};
stream.push(str);
stream.push(null);
return stream;
}

View File

@ -1,10 +1,7 @@
module Test.HTTPure.TestHelpers where
import Prelude
import Effect (Effect)
import Effect.Aff (Aff, makeAff, nonCanceler)
import Effect.Class (liftEffect)
import Effect.Ref (new, modify_, read)
import Data.Array (fromFoldable) as Array
import Data.Either (Either(Right))
import Data.List (List(Nil, Cons), reverse)
@ -12,29 +9,33 @@ import Data.Maybe (fromMaybe)
import Data.Options ((:=))
import Data.String (toLower)
import Data.Tuple (Tuple)
import Foreign.Object (fromFoldable) as Object
import Effect (Effect)
import Effect.Aff (Aff, makeAff, nonCanceler)
import Effect.Class (liftEffect)
import Effect.Ref (modify_, new, read)
import Foreign.Object (Object, lookup)
import Foreign.Object (fromFoldable) as Object
import Node.Buffer (Buffer, concat, create, fromString)
import Node.Buffer (toString) as Buffer
import Node.Buffer (Buffer, create, fromString, concat)
import Node.Encoding (Encoding(UTF8))
import Node.HTTP (Response) as HTTP
import Node.HTTP (Request)
import Node.HTTP.Client (Response, request) as HTTPClient
import Node.HTTP (Response) as HTTP
import Node.HTTP.Client
( RequestHeaders(RequestHeaders)
, requestAsStream
, protocol
, method
, hostname
, port
, path
, headers
, hostname
, method
, path
, port
, protocol
, rejectUnauthorized
, statusCode
, responseHeaders
, requestAsStream
, responseAsStream
, responseHeaders
, statusCode
)
import Node.Stream (Readable, write, end, onData, onEnd)
import Node.HTTP.Client (Response, request) as HTTPClient
import Node.Stream (Readable, end, onData, onEnd, write)
import Test.Spec (Spec)
import Test.Spec.Assertions (shouldEqual)
import Unsafe.Coerce (unsafeCoerce)
@ -60,11 +61,12 @@ request ::
request secure port' method' headers' path' body =
makeAff \done -> do
req <- HTTPClient.request options $ Right >>> done
let
stream = requestAsStream req
let stream = requestAsStream req
void
$ write stream body
$ const
$ end stream
$ const
$ pure unit
pure nonCanceler
where

View File

@ -1,8 +1,8 @@
module Test.HTTPure.UtilsSpec where
import Test.Spec (describe, it)
import HTTPure.Utils (replacePlus)
import Test.HTTPure.TestHelpers (Test, (?=))
import Test.Spec (describe, it)
replacePlusSpec :: Test
replacePlusSpec =

View File

@ -1,12 +1,13 @@
module Test.HTTPure.VersionSpec where
import Prelude
import Test.Spec (describe, it)
import HTTPure.Version
( Version(HTTP0_9, HTTP1_0, HTTP1_1, HTTP2_0, HTTP3_0, Other)
, read
)
import Test.HTTPure.TestHelpers (Test, (?=), mockRequest)
import Test.HTTPure.TestHelpers (Test, mockRequest, (?=))
import Test.Spec (describe, it)
showSpec :: Test
showSpec =

View File

@ -1,12 +1,11 @@
module Test.Main where
import Prelude
import Effect.Aff (launchAff_)
import Test.Spec (describe)
import Test.Spec.Reporter (specReporter)
import Test.Spec.Runner (runSpec)
import Test.HTTPure.BodySpec (bodySpec)
import Test.HTTPure.HeadersSpec (headersSpec)
import Test.HTTPure.IntegrationSpec (integrationSpec)
import Test.HTTPure.LookupSpec (lookupSpec)
import Test.HTTPure.MethodSpec (methodSpec)
import Test.HTTPure.PathSpec (pathSpec)
@ -15,10 +14,12 @@ import Test.HTTPure.RequestSpec (requestSpec)
import Test.HTTPure.ResponseSpec (responseSpec)
import Test.HTTPure.ServerSpec (serverSpec)
import Test.HTTPure.StatusSpec (statusSpec)
import Test.HTTPure.TestHelpers (TestSuite)
import Test.HTTPure.UtilsSpec (utilsSpec)
import Test.HTTPure.VersionSpec (versionSpec)
import Test.HTTPure.IntegrationSpec (integrationSpec)
import Test.HTTPure.TestHelpers (TestSuite)
import Test.Spec (describe)
import Test.Spec.Reporter (specReporter)
import Test.Spec.Runner (runSpec)
main :: TestSuite
main = launchAff_ $ runSpec [ specReporter ] $ describe "HTTPure" do