fix: migrate to new tooling

This commit is contained in:
orion 2023-12-13 15:51:23 -06:00
parent 611a0be19e
commit 0b38030bf1
Signed by: orion
GPG Key ID: 6D4165AE4C928719
59 changed files with 412 additions and 591 deletions

View File

@ -1,46 +0,0 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up a PureScript toolchain
uses: purescript-contrib/setup-purescript@main
with:
purescript: "unstable"
purs-tidy: "latest"
- name: Cache PureScript dependencies
uses: actions/cache@v2
with:
key: ${{ runner.os }}-spago-${{ hashFiles('**/*.dhall') }}
path: |
.spago
output
- name: Install dependencies
run: spago install
- name: Build source
run: spago build --no-install --purs-args '--censor-lib --strict'
- name: Run tests
run: spago -x test.dhall test
- name: Check formatting
run: purs-tidy check src test
- name: Verify Bower & Pulp
if: ${{ github.ref == 'refs/heads/main' }}
run: |
npm install bower pulp@16.0.1
npx bower install
npx pulp build -- --censor-lib --strict

8
.prettierrc.cjs Normal file
View File

@ -0,0 +1,8 @@
module.exports = {
tabWidth: 2,
trailingComma: 'all',
singleQuote: true,
semi: false,
arrowParens: 'avoid',
plugins: [],
}

View File

@ -1,10 +0,0 @@
{
"importSort": "ide",
"importWrap": "source",
"indent": 2,
"operatorsFile": null,
"ribbon": 1,
"typeArrowPlacement": "last",
"unicode": "never",
"width": null
}

3
.tool-versions Normal file
View File

@ -0,0 +1,3 @@
bun 1.0.11
purescript 0.15.12
nodejs 20.9.0

View File

@ -1,61 +0,0 @@
{
"name": "purescript-httpurple",
"license": [
"MIT"
],
"repository": {
"type": "git",
"url": "https://github.com/sigma-andex/purescript-httpurple.git"
},
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"output"
],
"dependencies": {
"purescript-aff": "^v7.1.0",
"purescript-arrays": "^v7.2.1",
"purescript-bifunctors": "^v6.0.0",
"purescript-console": "^v6.0.0",
"purescript-control": "^v6.0.0",
"purescript-debug": "^v6.0.2",
"purescript-effect": "^v4.0.0",
"purescript-either": "^v6.1.0",
"purescript-exceptions": "^v6.0.0",
"purescript-foldable-traversable": "^v6.0.0",
"purescript-foreign": "^v7.0.0",
"purescript-foreign-object": "^v4.1.0",
"purescript-functions": "^v6.0.0",
"purescript-js-uri": "https://github.com/purescript-contrib/purescript-js-uri.git#v3.1.0",
"purescript-justifill": "https://github.com/i-am-the-slime/purescript-justifill.git#v0.5.0",
"purescript-lists": "^v7.0.0",
"purescript-literals": "https://github.com/rowtype-yoga/purescript-literals.git#v1.0.2",
"purescript-maybe": "^v6.0.0",
"purescript-newtype": "^v5.0.0",
"purescript-node-buffer": "^v9.0.0",
"purescript-node-event-emitter": "https://github.com/purescript-node/purescript-node-event-emitter.git#v3.0.0",
"purescript-node-fs": "^v9.1.0",
"purescript-node-http": "^v9.1.0",
"purescript-node-net": "^v5.1.0",
"purescript-node-process": "^v11.2.0",
"purescript-node-streams": "^v9.0.0",
"purescript-options": "^v7.0.0",
"purescript-ordered-collections": "^v3.0.0",
"purescript-posix-types": "^v6.0.0",
"purescript-prelude": "^v6.0.1",
"purescript-profunctor": "^v6.0.0",
"purescript-record": "^v4.0.0",
"purescript-record-studio": "https://github.com/rowtype-yoga/purescript-record-studio.git#v1.0.4",
"purescript-refs": "^v6.0.0",
"purescript-routing-duplex": "^v0.7.0",
"purescript-safe-coerce": "^v2.0.0",
"purescript-strings": "^v6.0.1",
"purescript-transformers": "^v6.0.0",
"purescript-tuples": "^v7.0.0",
"purescript-type-equality": "^v4.0.1",
"purescript-typelevel-prelude": "^v7.0.0",
"purescript-unsafe-coerce": "^v6.0.0",
"purescript-untagged-union": "https://github.com/rowtype-yoga/purescript-untagged-union.git#v1.0.0"
}
}

27
bun/fmt.js Normal file
View File

@ -0,0 +1,27 @@
/** @type {(parser: string, ps: string[]) => import("bun").Subprocess} */
const prettier = (parser, ps) =>
Bun.spawn(['bun', 'x', 'prettier', '--write', '--parser', parser, ...ps], {
stdout: 'inherit',
stderr: 'inherit',
})
const procs = [
prettier('babel', ['./src/**/*.js', './bun/**/*.js', './.prettierrc.cjs']),
prettier('json', ['./package.json', './jsconfig.json']),
Bun.spawn(
[
'bun',
'x',
'purs-tidy',
'format-in-place',
'src/**/*.purs',
'test/**/*.purs',
],
{
stdout: 'inherit',
stderr: 'inherit',
},
),
]
await Promise.all(procs.map(p => p.exited))

16
jsconfig.json Normal file
View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"types": ["bun-types"],
"lib": ["esnext"],
"target": "esnext",
"module": "esnext",
"moduleResolution": "bundler",
"moduleDetection": "force",
"jsx": "react",
"allowJs": true,
"checkJs": true,
"noEmit": true,
"strict": true
},
"include": ["src/**/*.js", "bun/**/*.js"]
}

View File

@ -1,155 +0,0 @@
let upstream =
https://github.com/purescript/package-sets/releases/download/psc-0.15.10-20230804/packages.dhall
sha256:85d0df546868128fdbd6b4fc309252d69d2c0e99090c7e8ad80e97986f4d9ac4
in upstream
with node-event-emitter.version = "v3.0.0"
with node-event-emitter.dependencies =
[ "effect"
, "either"
, "functions"
, "maybe"
, "nullable"
, "prelude"
, "unsafe-coerce"
]
with node-buffer.version = "v9.0.0"
with node-buffer.dependencies =
[ "arraybuffer-types"
, "effect"
, "maybe"
, "st"
, "unsafe-coerce"
, "nullable"
]
with node-fs.version = "v9.1.0"
with node-fs.dependencies =
[ "datetime"
, "effect"
, "either"
, "enums"
, "exceptions"
, "functions"
, "integers"
, "js-date"
, "maybe"
, "node-buffer"
, "node-path"
, "node-streams"
, "nullable"
, "partial"
, "prelude"
, "strings"
, "unsafe-coerce"
]
with node-streams.version = "v9.0.0"
with node-streams.dependencies =
[ "aff"
, "effect"
, "exceptions"
, "maybe"
, "node-buffer"
, "node-event-emitter"
, "nullable"
, "prelude"
, "unsafe-coerce"
]
with node-process.version = "v11.2.0"
with node-process.dependencies =
[ "effect"
, "foreign-object"
, "foreign"
, "maybe"
, "node-streams"
, "node-event-emitter"
, "posix-types"
, "prelude"
, "unsafe-coerce"
]
with node-net.version = "v5.1.0"
with node-net.dependencies =
[ "console"
, "datetime"
, "effect"
, "exceptions"
, "maybe"
, "node-buffer"
, "node-event-emitter"
, "node-fs"
, "node-streams"
, "nullable"
, "partial"
, "prelude"
, "unsafe-coerce"
]
with node-url.version = "v7.0.0"
with node-url.dependencies =
[ "prelude"
, "effect"
, "foreign"
, "nullable"
, "tuples"
]
with node-zlib =
{ dependencies =
[ "aff"
, "console"
, "effect"
, "either"
, "functions"
, "node-buffer"
, "node-streams"
, "prelude"
, "unsafe-coerce"
]
, repo = "https://github.com/purescript-node/purescript-node-zlib.git"
, version = "v0.4.0"
}
with node-readline.version = "v8.1.0"
with node-readline.dependencies =
[ "effect"
, "foreign"
, "node-event-emitter"
, "node-process"
, "node-streams"
, "options"
, "prelude"
]
with node-tls =
{ dependencies =
[ "console"
, "effect"
, "either"
, "exceptions"
, "foreign"
, "maybe"
, "node-buffer"
, "node-event-emitter"
, "node-net"
, "node-streams"
, "nullable"
, "partial"
, "prelude"
, "unsafe-coerce"
]
, repo = "https://github.com/purescript-node/purescript-node-tls.git"
, version = "v0.3.1"
}
with node-http.version = "v9.1.0"
with node-http.dependencies =
[ "arraybuffer-types"
, "contravariant"
, "effect"
, "foreign"
, "foreign-object"
, "maybe"
, "node-buffer"
, "node-net"
, "node-streams"
, "node-tls"
, "node-url"
, "nullable"
, "options"
, "prelude"
, "unsafe-coerce"
]

View File

@ -1,83 +0,0 @@
{
sources ? import ./sources.nix,
nixpkgs ? sources.nixpkgs,
easy-purescript-nix ? sources.easy-purescript-nix,
alejandra ? sources.alejandra,
}: let
niv-overlay = self: super: {
niv = self.symlinkJoin {
name = "niv";
paths = [super.niv];
buildInputs = [self.makeWrapper];
postBuild = ''
wrapProgram $out/bin/niv \
--add-flags "--sources-file ${toString ./sources.json}"
'';
};
};
easy-purescript-nix-overlay = pkgs: _: {
inherit (import easy-purescript-nix {inherit pkgs;}) purescript purs-tidy spago psa pulp-16_0_0-0;
};
alejandra-overlay = self: _: {
alejandra = (import alejandra)."${self.system}";
};
pkgs = import nixpkgs {
overlays = [
niv-overlay
easy-purescript-nix-overlay
alejandra-overlay
];
};
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,38 +0,0 @@
{
"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": "0ad5775c1e80cdd952527db2da969982e39ff592",
"sha256": "0x53ads5v8zqsk4r1mfpzf5913byifdpv5shnvxpgw634ifyj1kg",
"type": "tarball",
"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": "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": "af911e8452bd05d40674bf603332f37480ceb03d",
"sha256": "1wxx4zdvqxfslqvx17jz1blndybx5jkqsp5rb5qyma1y59jsbpy3",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/af911e8452bd05d40674bf603332f37480ceb03d.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
}
}

View File

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

View File

@ -1,51 +0,0 @@
{ name = "httpurple"
, dependencies =
[ "aff"
, "arrays"
, "bifunctors"
, "console"
, "control"
, "debug"
, "effect"
, "either"
, "exceptions"
, "foldable-traversable"
, "foreign"
, "foreign-object"
, "functions"
, "js-uri"
, "justifill"
, "lists"
, "literals"
, "maybe"
, "newtype"
, "node-buffer"
, "node-event-emitter"
, "node-fs"
, "node-net"
, "node-http"
, "node-process"
, "node-streams"
, "options"
, "ordered-collections"
, "posix-types"
, "prelude"
, "profunctor"
, "record"
, "record-studio"
, "refs"
, "routing-duplex"
, "safe-coerce"
, "strings"
, "transformers"
, "tuples"
, "type-equality"
, "typelevel-prelude"
, "unsafe-coerce"
, "untagged-union"
]
, packages = ./packages.dhall
, sources = [ "src/**/*.purs" ]
, license = "MIT"
, repository = "https://github.com/sigma-andex/purescript-httpurple.git"
}

200
spago.yaml Normal file
View File

@ -0,0 +1,200 @@
package:
dependencies:
- aff
- arrays
- bifunctors
- console
- control
- debug
- effect
- either
- exceptions
- foldable-traversable
- foreign
- foreign-object
- functions
- js-uri
- justifill
- lists
- literals
- maybe
- newtype
- node-buffer
- node-event-emitter
- node-fs
- node-http
- node-net
- node-process
- node-streams
- options
- ordered-collections
- posix-types
- prelude
- profunctor
- record
- record-studio
- refs
- routing-duplex
- safe-coerce
- strings
- transformers
- tuples
- type-equality
- typelevel-prelude
- unsafe-coerce
- untagged-union
name: httpurple
test:
dependencies:
- node-child-process
- spec
main: Test.Main
workspace:
extra_packages:
node-buffer:
dependencies:
- arraybuffer-types
- effect
- maybe
- nullable
- st
- unsafe-coerce
git: https://github.com/purescript-node/purescript-node-buffer.git
ref: v9.0.0
node-fs:
dependencies:
- datetime
- effect
- either
- enums
- exceptions
- functions
- integers
- js-date
- maybe
- node-buffer
- node-path
- node-streams
- nullable
- partial
- prelude
- strings
- unsafe-coerce
git: https://github.com/purescript-node/purescript-node-fs.git
ref: v9.1.0
node-http:
dependencies:
- arraybuffer-types
- contravariant
- effect
- foreign
- foreign-object
- maybe
- node-buffer
- node-net
- node-streams
- node-tls
- node-url
- nullable
- options
- prelude
- unsafe-coerce
git: https://github.com/purescript-node/purescript-node-http.git
ref: v9.1.0
node-net:
dependencies:
- console
- datetime
- effect
- exceptions
- maybe
- node-buffer
- node-event-emitter
- node-fs
- node-streams
- nullable
- partial
- prelude
- unsafe-coerce
git: https://github.com/purescript-node/purescript-node-net.git
ref: v5.1.0
node-process:
dependencies:
- effect
- foreign
- foreign-object
- maybe
- node-event-emitter
- node-streams
- posix-types
- prelude
- unsafe-coerce
git: https://github.com/purescript-node/purescript-node-process.git
ref: v11.2.0
node-readline:
dependencies:
- effect
- foreign
- node-event-emitter
- node-process
- node-streams
- options
- prelude
git: https://github.com/purescript-node/purescript-node-readline.git
ref: v8.1.0
node-streams:
dependencies:
- aff
- effect
- exceptions
- maybe
- node-buffer
- node-event-emitter
- nullable
- prelude
- unsafe-coerce
git: https://github.com/purescript-node/purescript-node-streams.git
ref: v9.0.0
node-tls:
dependencies:
- console
- effect
- either
- exceptions
- foreign
- maybe
- node-buffer
- node-event-emitter
- node-net
- node-streams
- nullable
- partial
- prelude
- unsafe-coerce
git: https://github.com/purescript-node/purescript-node-tls.git
ref: v0.3.1
node-url:
dependencies:
- effect
- foreign
- nullable
- prelude
- tuples
git: https://github.com/purescript-node/purescript-node-url.git
ref: v7.0.0
node-zlib:
dependencies:
- aff
- console
- effect
- either
- functions
- node-buffer
- node-streams
- prelude
- unsafe-coerce
git: https://github.com/purescript-node/purescript-node-zlib.git
ref: v0.4.0
package_set:
url: https://raw.githubusercontent.com/purescript/package-sets/psc-0.15.10-20230930/packages.json
hash: sha256-nTsd44o7/hrTdk0c6dh0wyBqhFFDJJIeKdQU6L1zv/A=

View File

@ -1,17 +0,0 @@
let conf = ./spago.dhall
in conf // {
sources = conf.sources # [ "test/**/*.purs", "docs/Examples/**/*.purs" ],
dependencies = conf.dependencies # [
, "exceptions"
, "lists"
, "node-child-process"
, "spec"
, "debug"
, "transformers"
, "unsafe-coerce"
, "typelevel-prelude"
, "js-date"
, "nullable"
]
}

View File

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

View File

@ -2,6 +2,9 @@ module Test.HTTPurple.IntegrationSpec where
import Prelude
import Data.Newtype (wrap)
import Effect.Aff (delay)
import Effect.Aff.Class (liftAff)
import Effect.Class (liftEffect)
import Examples.AsyncResponse.Main as AsyncResponse
import Examples.BinaryRequest.Main as BinaryRequest
@ -31,25 +34,31 @@ asyncResponseSpec :: Test
asyncResponseSpec =
it "runs the async response example" do
close <- liftEffect AsyncResponse.main
response <- get 8080 empty "/"
liftAff $ delay $ wrap 200.0
response <- get 10000 empty "/"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
response ?= "hello world!"
binaryRequestSpec :: Test
binaryRequestSpec =
it "runs the binary request example" do
close <- liftEffect BinaryRequest.main
liftAff $ delay $ wrap 200.0
binaryBuf <- readFile BinaryResponse.filePath
response <- postBinary 8080 empty "/" binaryBuf
response <- postBinary 10000 empty "/" binaryBuf
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
response ?= "d5e776724dd545d8b54123b46362a553d10257cee688ef1be62166c984b34405"
binaryResponseSpec :: Test
binaryResponseSpec =
it "runs the binary response example" do
close <- liftEffect BinaryResponse.main
responseBuf <- getBinary 8080 empty "/"
liftAff $ delay $ wrap 200.0
responseBuf <- getBinary 10000 empty "/"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
binaryBuf <- readFile BinaryResponse.filePath
expected <- liftEffect $ toArray binaryBuf
response <- liftEffect $ toArray responseBuf
@ -59,8 +68,10 @@ chunkedSpec :: Test
chunkedSpec =
it "runs the chunked example" do
close <- liftEffect Chunked.main
response <- get 8080 empty "/"
liftAff $ delay $ wrap 200.0
response <- get 10000 empty "/"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
-- TODO this isn't a great way to validate this, we need a way of inspecting
-- each individual chunk instead of just looking at the entire response
response ?= "hello \nworld!\n"
@ -69,17 +80,21 @@ customStackSpec :: Test
customStackSpec =
it "runs the custom stack example" do
close <- liftEffect CustomStack.main
response <- get 8080 empty "/"
liftAff $ delay $ wrap 200.0
response <- get 10000 empty "/"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
response ?= "hello, joe"
headersSpec :: Test
headersSpec =
it "runs the headers example" do
close <- liftEffect Headers.main
header <- getHeader 8080 empty "/" "X-Example"
response <- get 8080 (singleton "X-Input" "test") "/"
liftAff $ delay $ wrap 200.0
header <- getHeader 10000 empty "/" "X-Example"
response <- get 10000 (singleton "X-Input" "test") "/"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
header ?= "hello world!"
response ?= "test"
@ -87,27 +102,33 @@ helloWorldSpec :: Test
helloWorldSpec =
it "runs the hello world example" do
close <- liftEffect HelloWorld.main
response <- get 8080 empty "/"
liftAff $ delay $ wrap 200.0
response <- get 10000 empty "/"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
response ?= "hello world!"
jsonParsingSpec :: Test
jsonParsingSpec =
it "runs the hello world example" do
close <- liftEffect JsonParsing.main
response <- post 8080 empty "/" "{\"name\":\"world\"}"
liftAff $ delay $ wrap 200.0
response <- post 10000 empty "/" "{\"name\":\"world\"}"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
response ?= "{\"hello\": \"world\" }"
middlewareSpec :: Test
middlewareSpec =
it "runs the middleware example" do
close <- liftEffect Middleware.main
header <- getHeader 8080 empty "/" "X-Middleware"
body <- get 8080 empty "/"
header' <- getHeader 8080 empty "/middleware" "X-Middleware"
body' <- get 8080 empty "/middleware"
liftAff $ delay $ wrap 200.0
header <- getHeader 10000 empty "/" "X-Middleware"
body <- get 10000 empty "/"
header' <- getHeader 10000 empty "/middleware" "X-Middleware"
body' <- get 10000 empty "/middleware"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
header ?= "router"
body ?= "hello"
header' ?= "middleware"
@ -117,9 +138,11 @@ multiRouteSpec :: Test
multiRouteSpec =
it "runs the multi route example" do
close <- liftEffect MultiRoute.main
hello <- get 8080 empty "/hello"
goodbye <- get 8080 empty "/goodbye"
liftAff $ delay $ wrap 200.0
hello <- get 10000 empty "/hello"
goodbye <- get 10000 empty "/goodbye"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
hello ?= "hello"
goodbye ?= "goodbye"
@ -127,9 +150,11 @@ pathSegmentsSpec :: Test
pathSegmentsSpec =
it "runs the path segments example" do
close <- liftEffect PathSegments.main
foo <- get 8080 empty "/segment/foo"
somebars <- get 8080 empty "/some/bars"
liftAff $ delay $ wrap 200.0
foo <- get 10000 empty "/segment/foo"
somebars <- get 10000 empty "/some/bars"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
foo ?= "foo"
somebars ?= "[\"some\",\"bars\"]"
@ -137,19 +162,23 @@ postSpec :: Test
postSpec =
it "runs the post example" do
close <- liftEffect Post.main
response <- post 8080 empty "/" "test"
liftAff $ delay $ wrap 200.0
response <- post 10000 empty "/" "test"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
response ?= "test"
queryParametersSpec :: Test
queryParametersSpec =
it "runs the query parameters example" do
close <- liftEffect QueryParameters.main
foo <- get 8080 empty "/?foo"
bar <- get 8080 empty "/?bar=test"
notbar <- get 8080 empty "/?bar=nottest"
baz <- get 8080 empty "/?baz=test"
liftAff $ delay $ wrap 200.0
foo <- get 10000 empty "/?foo"
bar <- get 10000 empty "/?bar=test"
notbar <- get 10000 empty "/?bar=nottest"
baz <- get 10000 empty "/?baz=test"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
foo ?= "foo"
bar ?= "bar"
notbar ?= ""
@ -159,18 +188,22 @@ sslSpec :: Test
sslSpec =
it "runs the ssl example" do
close <- liftEffect SSL.main
response <- get' 8080 empty "/"
liftAff $ delay $ wrap 200.0
response <- get' 10000 empty "/"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
response ?= "hello world!"
extensibleMiddlewareSpec :: Test
extensibleMiddlewareSpec =
it "runs the extensible middleware example" do
close <- liftEffect ExtensibleMiddleware.main
liftAff $ delay $ wrap 200.0
let headers = Object.singleton "X-Token" "123"
body <- get 8080 headers "/"
body' <- get 8080 empty "/"
body <- get 10000 headers "/"
body' <- get 10000 empty "/"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
body `shouldStartWith` "hello John Doe, it is"
body' `shouldStartWith` "hello anonymous, it is"
@ -178,10 +211,12 @@ nodeMiddlewareSpec :: Test
nodeMiddlewareSpec =
it "runs the node middleware example" do
close <- liftEffect NodeMiddleware.main
liftAff $ delay $ wrap 200.0
let headers = Object.singleton "X-Token" "123"
body <- get 8080 headers "/"
body' <- get 8080 empty "/"
body <- get 10000 headers "/"
body' <- get 10000 empty "/"
liftEffect $ close $ pure unit
liftAff $ delay $ wrap 200.0
body `shouldStartWith` "hello John Doe"
body' `shouldStartWith` "hello anonymous"

View File

@ -4,7 +4,9 @@ import Prelude
import Control.Monad.Except (throwError)
import Data.Generic.Rep (class Generic)
import Effect.Aff (Aff)
import Data.Newtype (wrap)
import Effect.Aff (Aff, delay)
import Effect.Aff.Class (liftAff)
import Effect.Class (liftEffect)
import Effect.Exception (error)
import Foreign.Object (empty)
@ -35,14 +37,15 @@ serveSpec :: Test
serveSpec =
describe "serve" do
it "boots a server on the given port" do
close <- liftEffect $ serve { hostname: "localhost", port: 8080 } { route, router: mockRouter }
out <- get 8080 empty "/test"
close <- liftEffect $ serve { hostname: "localhost", port: 10000 } { route, router: mockRouter }
out <- get 10000 empty "/test"
liftEffect $ close $ pure unit
out ?= "/test"
it "responds with a 500 upon unhandled exceptions" do
let router _ = throwError $ error "fail!"
close <- liftEffect $ serve { hostname: "localhost", port: 8080 } { route, router }
status <- getStatus 8080 empty "/test"
close <- liftEffect $ serve { hostname: "localhost", port: 10000 } { route, router }
liftAff $ delay $ wrap 200.0
status <- getStatus 10000 empty "/test"
liftEffect $ close $ pure unit
status ?= 500
@ -52,8 +55,9 @@ serve'Spec =
it "boots a server with the given options" do
close <-
liftEffect
$ serve { hostname: "localhost", port: 8080 } { route, router: mockRouter }
out <- get 8080 empty "/test"
$ serve { hostname: "localhost", port: 10000 } { route, router: mockRouter }
liftAff $ delay $ wrap 200.0
out <- get 10000 empty "/test"
liftEffect $ close $ pure unit
out ?= "/test"
@ -64,14 +68,16 @@ serveSecureSpec =
it "boots a server on the given port" do
close <-
liftEffect
$ serve { hostname: "localhost", port: 8080, certFile: "./test/Mocks/Certificate.cer", keyFile: "./test/Mocks/Key.key" } { route, router: mockRouter }
out <- get' 8080 empty "/test"
$ serve { hostname: "localhost", port: 10000, certFile: "./test/Mocks/Certificate.cer", keyFile: "./test/Mocks/Key.key" } { route, router: mockRouter }
liftAff $ delay $ wrap 200.0
out <- get' 10000 empty "/test"
liftEffect $ close $ pure unit
out ?= "/test"
describe "with invalid key and cert files" do
it "throws" do
expectError $ liftEffect
$ serve { hostname: "localhost", port: 8080, certFile: "", keyFile: "" } { route, router: mockRouter }
$ serve { hostname: "localhost", port: 10000, certFile: "", keyFile: "" } { route, router: mockRouter }
liftAff $ delay $ wrap 200.0
serveSecure'Spec :: Test
serveSecure'Spec =
@ -79,11 +85,12 @@ serveSecure'Spec =
describe "with valid key and cert files" do
it "boots a server on the given port" do
let
options = { hostname: "localhost", port: 8080, certFile: "./test/Mocks/Certificate.cer", keyFile: "./test/Mocks/Key.key" }
options = { hostname: "localhost", port: 10000, certFile: "./test/Mocks/Certificate.cer", keyFile: "./test/Mocks/Key.key" }
close <-
liftEffect
$ serve options { route, router: mockRouter }
out <- get' 8080 empty "/test"
liftAff $ delay $ wrap 200.0
out <- get' 10000 empty "/test"
liftEffect $ close $ pure unit
out ?= "/test"

View File

@ -1,4 +1,4 @@
import { Readable } from "stream";
import { Writable, Readable } from "stream";
export const mockRequestImpl = httpVersion => method => url => body => headers => () => {
const stream = new Readable({
@ -15,31 +15,25 @@ export const mockRequestImpl = httpVersion => method => url => body => headers =
return stream;
};
export const mockResponse = () => ({
body: "",
headers: {},
write: function (str, encoding, callback) {
this.body = this.body + str;
if (callback) {
callback();
}
export const mockResponse = () => {
const stream = new Writable({
write: function (chunk, _, cb) {
this.body += chunk;
if (cb) cb();
},
end: function (str, encoding, callback) {
if (callback) {
callback();
}
writev: function (chunks, _, cb) {
this.body += chunks.join('');
if (cb) cb();
},
})
stream.body = ''
stream.headers = {}
stream.setHeader = function (header, val) {
this.headers[header] = val;
}
on: function () {},
once: function () {},
emit: function () {},
setHeader: function (header, val) {
this.headers[header] = val;
},
});
return stream
}
export const stringToStream = str => {
const stream = new Readable();

View File

@ -23,7 +23,7 @@ route = RD.root $ RG.sum
-- | The path to the file containing the response to send
filePath :: String
filePath = "./docs/Examples/AsyncResponse/Hello"
filePath = "./test/example/AsyncResponse/Hello"
router :: Request Route -> Aff Response
router { route: SayHello } = readTextFile UTF8 filePath >>= ok
@ -31,12 +31,12 @@ router { route: SayHello } = readTextFile UTF8 filePath >>= ok
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route, router }
serve { hostname: "localhost", port: 10000, onStarted } { route, router }
where
onStarted = do
log " ┌────────────────────────────────────────────┐"
log " │ Server now up on port 8080 │"
log " │ Server now up on port 10000 │"
log " │ │"
log " │ To test, run: │"
log " │ > curl localhost:8080 # => hello world! │"
log " │ > curl localhost:10000 # => hello world! │"
log " └────────────────────────────────────────────┘"

View File

@ -29,13 +29,13 @@ router { body } = toBuffer body >>= sha256sum >>> ok
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route, router }
serve { hostname: "localhost", port: 10000, onStarted } { route, router }
where
onStarted = do
log " ┌─────────────────────────────────────────────────────────┐"
log " │ Server now up on port 8080 │"
log " │ Server now up on port 10000 │"
log " │ │"
log " │ To test, run: │"
log " │ > curl -XPOST --data-binary @circle.png localhost:8080 │"
log " │ > curl -XPOST --data-binary @circle.png localhost:10000 │"
log " │ # => d5e776724dd5... │"
log " └─────────────────────────────────────────────────────────┘"

View File

Before

Width:  |  Height:  |  Size: 453 B

After

Width:  |  Height:  |  Size: 453 B

View File

@ -34,12 +34,12 @@ router = const $ readFile filePath >>= ok' responseHeaders
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route, router }
serve { hostname: "localhost", port: 10000, onStarted } { route, router }
where
onStarted = do
log " ┌──────────────────────────────────────┐"
log " │ Server now up on port 8080 "
log " │ Server now up on port 10000"
log " │ │"
log " │ To test, run: │"
log " │ > curl -o circle.png localhost:8080 "
log " │ > curl -o circle.png localhost:10000"
log " └──────────────────────────────────────┘"

View File

Before

Width:  |  Height:  |  Size: 453 B

After

Width:  |  Height:  |  Size: 453 B

View File

@ -8,7 +8,7 @@ import Effect.Aff (Aff)
import Effect.Class (liftEffect)
import Effect.Console (log)
import HTTPurple (Request, Response, ServerM, ok, serve)
import Node.ChildProcess (defaultSpawnOptions, spawn, stdout)
import Node.ChildProcess (spawn, stdout)
import Node.Stream (Readable)
import Routing.Duplex as RD
import Routing.Duplex.Generic as RG
@ -25,7 +25,7 @@ route = RD.root $ RG.sum
-- | Run a script and return it's stdout stream
runScript :: String -> Aff (Readable ())
runScript script =
liftEffect $ stdout <$> spawn "sh" [ "-c", script ] defaultSpawnOptions
liftEffect $ stdout <$> spawn "sh" [ "-c", script ]
-- | Say 'hello world!' in chunks when run
router :: Request Route -> Aff Response
@ -34,14 +34,14 @@ router = const $ runScript "echo 'hello '; sleep 1; echo 'world!'" >>= ok
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route, router }
serve { hostname: "localhost", port: 10000, onStarted } { route, router }
where
onStarted = do
log " ┌──────────────────────────────────────┐"
log " │ Server now up on port 8080 "
log " │ Server now up on port 10000"
log " │ │"
log " │ To test, run: │"
log " │ > curl -Nv localhost:8080 "
log " │ > curl -Nv localhost:10000"
log " │ # => ... │"
log " │ # => < Transfer-Encoding: chunked │"
log " │ # => ... │"

View File

@ -31,13 +31,13 @@ sayHello _ = do
-- | Boot up the server
main :: ServerM
main =
serve' (\a -> runReaderT a {name: "joe"}) { hostname: "localhost", port: 8080, onStarted } { route, router: sayHello }
serve' (\a -> runReaderT a {name: "joe"}) { hostname: "localhost", port: 10000, onStarted } { route, router: sayHello }
where
onStarted = do
log " ┌───────────────────────────────────────┐"
log " │ Server now up on port 8080 "
log " │ Server now up on port 10000"
log " │ │"
log " │ To test, run: │"
log " │ > curl -v localhost:8080 "
log " │ > curl -v localhost:10000"
log " │ # => hello, joe │"
log " └───────────────────────────────────────┘"

1
test/example/Examples Symbolic link
View File

@ -0,0 +1 @@
docs/Examples

View File

@ -55,13 +55,13 @@ middlewareStack = authenticator <<< requestTime
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route: sayHelloRoute, router: middlewareStack sayHello }
serve { hostname: "localhost", port: 10000, onStarted } { route: sayHelloRoute, router: middlewareStack sayHello }
where
onStarted = do
log " ┌───────────────────────────────────────────────┐"
log " │ Server now up on port 8080 │"
log " │ Server now up on port 10000 │"
log " │ │"
log " │ To test, run: │"
log " │ > http -v GET localhost:8080 X-Token:123 │"
log " │ > http -v GET localhost:10000 X-Token:123 │"
log " │ # => hello John Doe, it is ... │"
log " └───────────────────────────────────────────────┘"

View File

@ -33,14 +33,14 @@ router { headers } = ok' responseHeaders $ headers !@ "X-Input"
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route, router }
serve { hostname: "localhost", port: 10000, onStarted } { route, router }
where
onStarted = do
log " ┌──────────────────────────────────────────────┐"
log " │ Server now up on port 8080 "
log " │ Server now up on port 10000"
log " │ │"
log " │ To test, run: │"
log " │ > curl -H 'X-Input: test' -v localhost:8080 "
log " │ > curl -H 'X-Input: test' -v localhost:10000"
log " │ # => ... │"
log " │ # => ...< X-Example: hello world! │"
log " │ # => ... │"

View File

@ -20,12 +20,12 @@ route = RD.root $ RG.sum
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route, router: const $ ok "hello world!" }
serve { hostname: "localhost", port: 10000, onStarted } { route, router: const $ ok "hello world!" }
where
onStarted = do
log " ┌────────────────────────────────────────────┐"
log " │ Server now up on port 8080 "
log " │ Server now up on port 10000"
log " │ │"
log " │ To test, run: │"
log " │ > curl localhost:8080 # => hello world! "
log " │ > curl localhost:10000 # => hello world!"
log " └────────────────────────────────────────────┘"

View File

@ -46,7 +46,7 @@ testEncoder = JsonEncoder $ \{ hello } -> "{\"hello\": \"" <> hello <> "\" }"
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route, router }
serve { hostname: "localhost", port: 10000, onStarted } { route, router }
where
router { route: SayHello, method: Post, body } = usingCont do
-- in your project you will want to use Argonaut.jsonDecoder from httpurple-argonaut
@ -57,9 +57,9 @@ main =
onStarted = do
log " ┌────────────────────────────────────────────┐"
log " │ Server now up on port 8080 │"
log " │ Server now up on port 10000 │"
log " │ │"
log " │ To test, run: │"
log " │ > http -v POST localhost:8080 hello=world │"
log " │ > http -v POST localhost:10000 hello=world │"
log " | # => { \"hello\": \"world\" } │"
log " └────────────────────────────────────────────┘"

View File

@ -81,19 +81,19 @@ middlewareStack = loggingMiddleware <<< headerMiddleware <<< pathMiddleware
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route: middlewareRoute <+> sayHelloRoute, router: middlewareStack sayHello }
serve { hostname: "localhost", port: 10000, onStarted } { route: middlewareRoute <+> sayHelloRoute, router: middlewareStack sayHello }
where
onStarted = do
log " ┌───────────────────────────────────────┐"
log " │ Server now up on port 8080 "
log " │ Server now up on port 10000"
log " │ │"
log " │ To test, run: │"
log " │ > curl -v localhost:8080 "
log " │ > curl -v localhost:10000"
log " │ # => ... │"
log " │ # => ...< X-Middleware: router │"
log " │ # => ... │"
log " │ # => hello │"
log " │ > curl -v localhost:8080/middleware "
log " │ > curl -v localhost:10000/middleware"
log " │ # => ... │"
log " │ # => ...< X-Middleware: middleware │"
log " │ # => ... │"

View File

@ -30,15 +30,15 @@ router { route: GoodBye } = ok "goodbye"
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route, router }
serve { hostname: "localhost", port: 10000, onStarted } { route, router }
where
onStarted = do
log " ┌────────────────────────────────┐"
log " │ Server now up on port 8080 "
log " │ Server now up on port 80000"
log " │ │"
log " │ To test, run: │"
log " │ > curl localhost:8080/hello │"
log " │ > curl localhost:10000/hello │"
log " │ # => hello │"
log " │ > curl localhost:8080/goodbye │"
log " │ > curl localhost:10000/goodbye │"
log " │ # => goodbye │"
log " └────────────────────────────────┘"

View File

@ -39,13 +39,13 @@ sayHello { user } = case Nullable.toMaybe user of
-- | Boot up the server
main :: ServerM
main =
serveNodeMiddleware { hostname: "localhost", port: 8080, onStarted } { route: sayHelloRoute, router: sayHello, nodeMiddleware }
serveNodeMiddleware { hostname: "localhost", port: 10000, onStarted } { route: sayHelloRoute, router: sayHello, nodeMiddleware }
where
onStarted = do
log " ┌───────────────────────────────────────────────┐"
log " │ Server now up on port 8080 │"
log " │ Server now up on port 10000 │"
log " │ │"
log " │ To test, run: │"
log " │ > http -v GET localhost:8080 X-Token:123 │"
log " │ > http -v GET localhost:10000 X-Token:123 │"
log " │ # => hello John Doe │"
log " └───────────────────────────────────────────────┘"

View File

@ -30,15 +30,15 @@ router { route: ManySegments elems } = ok $ show elems
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route, router }
serve { hostname: "localhost", port: 10000, onStarted } { route, router }
where
onStarted = do
log " ┌───────────────────────────────────────────────┐"
log " │ Server now up on port 8080 │"
log " │ Server now up on port 10000 │"
log " │ │"
log " │ To test, run: │"
log " │ > curl localhost:8080/segment/<anything> │"
log " │ > curl localhost:10000/segment/<anything> │"
log " │ # => <anything> │"
log " │ > curl localhost:8080/<anything>/<else>/... │"
log " │ > curl localhost:10000/<anything>/<else>/... │"
log " │ # => [ <anything>, <else>, ... ] │"
log " └───────────────────────────────────────────────┘"

View File

@ -28,13 +28,13 @@ router _ = notFound
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route, router }
serve { hostname: "localhost", port: 10000, onStarted } { route, router }
where
onStarted = do
log " ┌───────────────────────────────────────────┐"
log " │ Server now up on port 8080 "
log " │ Server now up on port 10000"
log " │ │"
log " │ To test, run: │"
log " │ > curl -XPOST --data test localhost:8080 "
log " │ > curl -XPOST --data test localhost:10000"
log " │ # => test │"
log " └───────────────────────────────────────────┘"

View File

@ -32,18 +32,18 @@ router _ = notFound
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, onStarted } { route, router }
serve { hostname: "localhost", port: 10000, onStarted } { route, router }
where
onStarted :: Effect Unit
onStarted = do
log " ┌───────────────────────────────────────┐"
log " │ Server now up on port 8080 │"
log " │ Server now up on port 10000 │"
log " │ │"
log " │ To test, run: │"
log " │ > curl localhost:8080?foo "
log " │ > curl localhost:10000?foo"
log " │ # => foo │"
log " │ > curl localhost:8080?bar=test "
log " │ > curl localhost:10000?bar=test"
log " │ # => bar │"
log " │ > curl localhost:8080?baz=<anything> "
log " │ > curl localhost:10000?baz=<anything>"
log " │ # => <anything> │"
log " └───────────────────────────────────────┘"

View File

@ -23,11 +23,11 @@ route = RD.root $ G.sum
-- | The path to the certificate file
cert :: String
cert = "./docs/Examples/SSL/Certificate.cer"
cert = "./example/SSL/Certificate.cer"
-- | The path to the key file
key :: String
key = "./docs/Examples/SSL/Key.key"
key = "./example/SSL/Key.key"
-- | Say 'hello world!' when run
sayHello :: Request Route -> Aff Response
@ -36,14 +36,14 @@ sayHello _ = ok "hello world!"
-- | Boot up the server
main :: ServerM
main =
serve { hostname: "localhost", port: 8080, certFile: cert, keyFile: key, onStarted } { route, router: sayHello }
serve { hostname: "localhost", port: 10000, certFile: cert, keyFile: key, onStarted } { route, router: sayHello }
where
onStarted =
do
log " ┌───────────────────────────────────────────┐"
log " │ Server now up on port 8080 "
log " │ Server now up on port 10000"
log " │ │"
log " │ To test, run: │"
log " │ > curl --insecure https://localhost:8080 "
log " │ > curl --insecure https://localhost:10000"
log " │ # => hello world! │"
log " └───────────────────────────────────────────┘"