diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06ed895..95ed32d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [master] + branches: [main] pull_request: - branches: [master] + branches: [main] jobs: build: @@ -18,7 +18,7 @@ jobs: - uses: actions/setup-node@v2 with: - node-version: "14" + node-version: "20" - name: Install dependencies run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index bc1b2e0..edb68a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,14 @@ Notable changes to this project are documented in this file. The format is based Breaking changes: - Migrated from `web-promise` to `js-promise` +- Fork from web-fetch +- Rename package +- Replaced web-promise dependency with js-promise + New features: +- Improve request options to only use the provided attributes instead of a full options record using default values + Bugfixes: Other improvements: @@ -16,6 +22,7 @@ Other improvements: ## [v3.0.0](https://github.com/purescript-web/purescript-web-fetch/releases/tag/v3.0.0) - 2022-04-27 Breaking changes: + - Migrate FFI to ES modules (#8 by @JordanMartinez) New features: @@ -27,6 +34,7 @@ Other improvements: ## [v2.0.0](https://github.com/purescript-web/purescript-web-fetch/releases/tag/v2.0.0) - 2021-02-26 Breaking changes: + - Added support for PureScript 0.14 and dropped support for all previous versions (#2) New features: @@ -34,6 +42,7 @@ New features: Bugfixes: Other improvements: + - Migrated CI to GitHub Actions and updated installation instructions to use Spago (#1) - Added a CHANGELOG.md file and pull request template (#3, #4) diff --git a/README.md b/README.md index 359bcb1..cc2aa33 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,19 @@ -# purescript-web-fetch +# purescript-fetch-core -[![Latest release](http://img.shields.io/github/release/purescript-web/purescript-web-fetch.svg)](https://github.com/purescript-web/purescript-web-fetch/releases) -[![Build status](https://github.com/purescript-web/purescript-web-fetch/workflows/CI/badge.svg?branch=master)](https://github.com/purescript-web/purescript-web-fetch/actions?query=workflow%3ACI+branch%3Amaster) -[![Pursuit](https://pursuit.purescript.org/packages/purescript-web-fetch/badge)](https://pursuit.purescript.org/packages/purescript-web-fetch) +[![Latest release](http://img.shields.io/github/release/purescript-contrib/purescript-js-fetch.svg)](https://github.com/purescript-contrib/purescript-js-fetch/releases) +[![Build status](https://github.com/purescript-contrib/purescript-js-fetch/workflows/CI/badge.svg?branch=master)](https://github.com/purescript-contrib/purescript-js-fetch/actions?query=workflow%3ACI+branch%3Amaster) +[![Pursuit](https://pursuit.purescript.org/packages/purescript-fetch-core/badge)](https://pursuit.purescript.org/packages/purescript-fetch-core) Types and low-level implementations for the [WHATWG Fetch Living Standard](https://fetch.spec.whatwg.org/). +For a high-level library see [`purescript-fetch`](https://github.com/rowtype-yoga/purescript-fetch) + ## Installation ``` -spago install web-fetch +spago install js-fetch ``` ## Documentation -Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-web-fetch). +Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-js-fetch). diff --git a/bower.json b/bower.json index 15e3937..51a6d76 100644 --- a/bower.json +++ b/bower.json @@ -1,28 +1,39 @@ { - "name": "purescript-web-fetch", - "homepage": "https://github.com/purescript-web/purescript-web-fetch", - "license": "MIT", + "name": "purescript-fetch-core", + "license": [ + "MIT" + ], "repository": { "type": "git", - "url": "https://github.com/purescript-web/purescript-web-fetch.git" + "url": "https://github.com/purescript-contrib/purescript-js-fetch.git" }, "ignore": [ "**/.*", - "bower_components", "node_modules", - "output", - "bower.json", - "package.json" + "bower_components", + "output" ], "dependencies": { - "purescript-effect": "^4.0.0", - "purescript-foreign-object": "^4.0.0", - "purescript-http-methods": "^6.0.0", - "purescript-js-promise": "https://github.com/purescript-contrib/purescript-js-promise.git#^1.0.0", - "purescript-prelude": "^6.0.0", - "purescript-record": "^4.0.0", - "purescript-typelevel-prelude": "^7.0.0", - "purescript-web-file": "^4.0.0", - "purescript-web-streams": "https://github.com/purescript-web/purescript-web-streams.git#^4.0.0" + "purescript-arraybuffer-types": "^v3.0.2", + "purescript-arrays": "^v7.0.0", + "purescript-console": "^v6.0.0", + "purescript-effect": "^v4.0.0", + "purescript-foldable-traversable": "^v6.0.0", + "purescript-foreign": "^v7.0.0", + "purescript-foreign-object": "^v4.0.0", + "purescript-functions": "^v6.0.0", + "purescript-http-methods": "^v6.0.0", + "purescript-js-promise": "https://github.com/purescript-contrib/purescript-js-promise.git#^v1.0.0", + "purescript-maybe": "^v6.0.0", + "purescript-newtype": "^v5.0.0", + "purescript-nullable": "^v6.0.0", + "purescript-prelude": "^v6.0.0", + "purescript-record": "^v4.0.0", + "purescript-tuples": "^v7.0.0", + "purescript-typelevel-prelude": "^v7.0.0", + "purescript-unfoldable": "^v6.0.0", + "purescript-unsafe-coerce": "^v6.0.0", + "purescript-web-file": "^v4.0.0", + "purescript-web-streams": "https://github.com/purescript-web/purescript-web-streams.git#^v4.0.0" } } diff --git a/package.json b/package.json index 532609e..356af3a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "private": true, "scripts": { "clean": "rimraf output && rimraf .pulp-cache", - "build": "eslint src && purs-tidy check --config-require src/**/*.purs && pulp build -- --censor-lib --strict" + "build": "eslint src && purs-tidy check --config-require src/**/*.purs && pulp build -- --censor-lib --strict", + "test": "spago -x test.dhall test" }, "devDependencies": { "eslint": "^8.28.0", diff --git a/packages.dhall b/packages.dhall new file mode 100644 index 0000000..6fafe74 --- /dev/null +++ b/packages.dhall @@ -0,0 +1,5 @@ +let upstream = + https://github.com/purescript/package-sets/releases/download/psc-0.15.10-20230803/packages.dhall + sha256:7da82e40277c398fd70f16af6450fb74287a88e2a3c8885c065dcdb9df893761 + +in upstream diff --git a/spago.dhall b/spago.dhall new file mode 100644 index 0000000..09f3399 --- /dev/null +++ b/spago.dhall @@ -0,0 +1,26 @@ +{ name = "fetch-core" +, dependencies = + [ "arraybuffer-types" + , "arrays" + , "effect" + , "foldable-traversable" + , "foreign" + , "foreign-object" + , "functions" + , "http-methods" + , "js-promise" + , "maybe" + , "newtype" + , "prelude" + , "record" + , "tuples" + , "typelevel-prelude" + , "unfoldable" + , "web-file" + , "web-streams" + ] +, packages = ./packages.dhall +, sources = [ "src/**/*.purs" ] +, license = "MIT" +, repository = "https://github.com/purescript-contrib/purescript-js-fetch.git" +} diff --git a/src/Web/Fetch.js b/src/JS/Fetch/Core.js similarity index 100% rename from src/Web/Fetch.js rename to src/JS/Fetch/Core.js diff --git a/src/Web/Fetch.purs b/src/JS/Fetch/Core.purs similarity index 75% rename from src/Web/Fetch.purs rename to src/JS/Fetch/Core.purs index bb05340..2157646 100644 --- a/src/Web/Fetch.purs +++ b/src/JS/Fetch/Core.purs @@ -1,4 +1,4 @@ -module Web.Fetch +module JS.Fetch ( FetchOptions , fetch , fetchWithOptions @@ -6,11 +6,11 @@ module Web.Fetch import Effect (Effect) import Effect.Uncurried (EffectFn2, runEffectFn2) +import JS.Fetch.AbortController (AbortSignal) +import JS.Fetch.Request (Request) +import JS.Fetch.Response (Response) import Prim.Row as Row import Promise (Promise) -import Web.Fetch.AbortController (AbortSignal) -import Web.Fetch.Request (Request) -import Web.Fetch.Response (Response) type FetchOptions = ( keepalive :: Boolean @@ -23,4 +23,4 @@ fetch :: Request -> Effect (Promise Response) fetch req = runEffectFn2 _fetch req {} fetchWithOptions :: forall r rx. Row.Union r rx FetchOptions => Request -> { | r } -> Effect (Promise Response) -fetchWithOptions = runEffectFn2 _fetch \ No newline at end of file +fetchWithOptions = runEffectFn2 _fetch diff --git a/src/Web/Fetch/AbortController.js b/src/JS/Fetch/Fetch/AbortController.js similarity index 100% rename from src/Web/Fetch/AbortController.js rename to src/JS/Fetch/Fetch/AbortController.js diff --git a/src/Web/Fetch/AbortController.purs b/src/JS/Fetch/Fetch/AbortController.purs similarity index 71% rename from src/Web/Fetch/AbortController.purs rename to src/JS/Fetch/Fetch/AbortController.purs index bf929b5..9a16b25 100644 --- a/src/Web/Fetch/AbortController.purs +++ b/src/JS/Fetch/Fetch/AbortController.purs @@ -1,4 +1,4 @@ -module Web.Fetch.AbortController where +module JS.Fetch.AbortController where import Effect (Effect) import Prelude (Unit) @@ -11,4 +11,4 @@ foreign import new :: Effect AbortController foreign import abort :: AbortController -> Effect Unit -foreign import signal :: AbortController -> AbortSignal \ No newline at end of file +foreign import signal :: AbortController -> AbortSignal diff --git a/src/JS/Fetch/Fetch/Duplex.purs b/src/JS/Fetch/Fetch/Duplex.purs new file mode 100644 index 0000000..808d14c --- /dev/null +++ b/src/JS/Fetch/Fetch/Duplex.purs @@ -0,0 +1,21 @@ +module JS.Fetch.Duplex where + +import Prelude + +import Data.Maybe (Maybe(..)) + +data Duplex = Half | Full + +derive instance Eq Duplex +derive instance Ord Duplex + +toString :: Duplex -> String +toString = case _ of + Half -> "half" + Full -> "full" + +fromString :: String -> Maybe Duplex +fromString = case _ of + "full" -> Just Full + "half" -> Just Half + _ -> Nothing diff --git a/src/Web/Fetch/Headers.js b/src/JS/Fetch/Fetch/Headers.js similarity index 100% rename from src/Web/Fetch/Headers.js rename to src/JS/Fetch/Fetch/Headers.js diff --git a/src/Web/Fetch/Headers.purs b/src/JS/Fetch/Fetch/Headers.purs similarity index 87% rename from src/Web/Fetch/Headers.purs rename to src/JS/Fetch/Fetch/Headers.purs index 185a4ad..46c0aa9 100644 --- a/src/Web/Fetch/Headers.purs +++ b/src/JS/Fetch/Fetch/Headers.purs @@ -1,4 +1,4 @@ -module Web.Fetch.Headers +module JS.Fetch.Headers ( Headers , fromFoldable , fromRecord @@ -36,9 +36,13 @@ foreign import fromObject :: Object String -> Headers fromFoldable :: forall f. Foldable f => f (Tuple String String) -> Headers fromFoldable f = unsafePerformEffect do init <- unsafeNew - foldM (\headers (Tuple key value) -> do - runEffectFn3 unsafeAppend key value headers - pure headers) init f + foldM + ( \headers (Tuple key value) -> do + runEffectFn3 unsafeAppend key value headers + pure headers + ) + init + f fromRecord :: forall r. Homogeneous r String => { | r } -> Headers fromRecord = unsafeFromRecord @@ -50,4 +54,4 @@ toUnfoldable :: forall f. Unfoldable f => Headers -> f (Tuple String String) toUnfoldable = Array.toUnfoldable <<< toArray empty :: Headers -empty = fromFoldable [] \ No newline at end of file +empty = fromFoldable [] diff --git a/src/JS/Fetch/Fetch/Integrity.purs b/src/JS/Fetch/Fetch/Integrity.purs new file mode 100644 index 0000000..6c4da73 --- /dev/null +++ b/src/JS/Fetch/Fetch/Integrity.purs @@ -0,0 +1,10 @@ +module JS.Fetch.Integrity where + +import Data.Newtype (class Newtype) +import Prelude (class Eq, class Ord) + +newtype Integrity = Integrity String + +derive instance Newtype Integrity _ +derive newtype instance Eq Integrity +derive newtype instance Ord Integrity diff --git a/src/Web/Fetch/Referrer.purs b/src/JS/Fetch/Fetch/Referrer.purs similarity index 78% rename from src/Web/Fetch/Referrer.purs rename to src/JS/Fetch/Fetch/Referrer.purs index edbfcff..7f41821 100644 --- a/src/Web/Fetch/Referrer.purs +++ b/src/JS/Fetch/Fetch/Referrer.purs @@ -1,4 +1,4 @@ -module Web.Fetch.Referrer where +module JS.Fetch.Referrer where data Referrer = ReferrerNone @@ -9,10 +9,10 @@ toString :: Referrer -> String toString = case _ of ReferrerNone -> "none" ReferrerClient -> "client" - ReferrerUrl url -> url + ReferrerUrl url -> url fromString :: String -> Referrer fromString = case _ of "none" -> ReferrerNone "client" -> ReferrerClient - url -> ReferrerUrl url \ No newline at end of file + url -> ReferrerUrl url diff --git a/src/Web/Fetch/ReferrerPolicy.purs b/src/JS/Fetch/Fetch/ReferrerPolicy.purs similarity index 96% rename from src/Web/Fetch/ReferrerPolicy.purs rename to src/JS/Fetch/Fetch/ReferrerPolicy.purs index e508b32..3b542de 100644 --- a/src/Web/Fetch/ReferrerPolicy.purs +++ b/src/JS/Fetch/Fetch/ReferrerPolicy.purs @@ -1,4 +1,4 @@ -module Web.Fetch.ReferrerPolicy where +module JS.Fetch.ReferrerPolicy where import Data.Maybe (Maybe(..)) diff --git a/src/Web/Fetch/Request.js b/src/JS/Fetch/Fetch/Request.js similarity index 100% rename from src/Web/Fetch/Request.js rename to src/JS/Fetch/Fetch/Request.js diff --git a/src/JS/Fetch/Fetch/Request.purs b/src/JS/Fetch/Fetch/Request.purs new file mode 100644 index 0000000..5ece006 --- /dev/null +++ b/src/JS/Fetch/Fetch/Request.purs @@ -0,0 +1,158 @@ +module JS.Fetch.Request + ( Request + , RequestOptions + , UnsafeRequestOptions + , _unsafeNew + , class ToInternal + , class ToInternalConverter + , class ToInternalHelper + , convert + , convertHelper + , convertImpl + , new + , unsafeNew + ) where + +import Prelude + +import Data.HTTP.Method (Method) +import Data.Newtype (un) +import Data.Symbol (class IsSymbol) +import Effect (Effect) +import Effect.Uncurried (EffectFn2, runEffectFn2) +import JS.Fetch.Duplex (Duplex) +import JS.Fetch.Duplex as Duplex +import JS.Fetch.Headers (Headers) +import JS.Fetch.Integrity (Integrity(..)) +import JS.Fetch.Referrer (Referrer) +import JS.Fetch.Referrer as Referrer +import JS.Fetch.ReferrerPolicy (ReferrerPolicy) +import JS.Fetch.ReferrerPolicy as ReferrerPolicy +import JS.Fetch.RequestBody (RequestBody) +import JS.Fetch.RequestCache (RequestCache) +import JS.Fetch.RequestCache as RequestCache +import JS.Fetch.RequestCredentials (RequestCredentials) +import JS.Fetch.RequestCredentials as RequestCredentials +import JS.Fetch.RequestMode (RequestMode) +import JS.Fetch.RequestMode as RequestMode +import Prim.Row (class Lacks, class Union) +import Prim.Row as R +import Prim.RowList as RL +import Record (delete, get, insert) +import Type.Proxy (Proxy(..)) + +foreign import data Request :: Type + +foreign import _unsafeNew :: forall r. EffectFn2 String { | r } Request + +type UnsafeRequestOptions = + ( method :: String + , headers :: Headers + , body :: RequestBody + , credentials :: String + , cache :: String + , mode :: String + , referrer :: String + , referrerPolicy :: String + , integrity :: String + , duplex :: String + ) + +type RequestOptions = + ( method :: Method + , headers :: Headers + , body :: RequestBody + , credentials :: RequestCredentials + , cache :: RequestCache + , mode :: RequestMode + , referrer :: Referrer + , referrerPolicy :: ReferrerPolicy + , integrity :: Integrity + , duplex :: Duplex + ) + +toUnsafeOptions + :: forall input output thruIn thruOut + . Union input thruIn RequestOptions + => Union output thruOut UnsafeRequestOptions + => ToInternal input output + => { | input } + -> { | output } +toUnsafeOptions = convert + +unsafeNew :: String -> { | UnsafeRequestOptions } -> Effect Request +unsafeNew = runEffectFn2 _unsafeNew + +new + :: forall input output thruIn thruOut + . Union input thruIn RequestOptions + => Union output thruOut UnsafeRequestOptions + => ToInternal input output + => String + -> { | input } + -> Effect Request +new url options = runEffectFn2 _unsafeNew url (toUnsafeOptions options) + +class ToInternal input output | input -> output where + convert :: Record input -> Record output + +instance (Union rIn thru RequestOptions, RL.RowToList rIn rInRL, ToInternalHelper rIn rInRL rOut) => ToInternal (| rIn) (| rOut) where + convert = convertHelper (Proxy :: Proxy rInRL) + +class ToInternalHelper :: forall k. Row Type -> k -> Row Type -> Constraint +class ToInternalHelper input inputRL output | inputRL -> output where + convertHelper :: Proxy inputRL -> Record input -> Record output + +instance ToInternalHelper r RL.Nil () where + convertHelper _ _ = {} +else instance + ( ToInternalConverter tpeIn tpeOut + , R.Cons sym tpeIn tailIn r + , RL.RowToList tailIn tailInRL + , Lacks sym tailIn + , IsSymbol sym + , ToInternalHelper tailIn tailInRL tailOutput + , R.Cons sym tpeOut tailOutput output + , Lacks sym tailOutput + ) => + ToInternalHelper r (RL.Cons sym tpeIn tailInRL) output where + convertHelper _ r = insert (Proxy :: Proxy sym) head tail + where + tail :: Record tailOutput + tail = delete (Proxy :: Proxy sym) r # convertHelper (Proxy :: Proxy tailInRL) + + head :: tpeOut + head = get (Proxy :: Proxy sym) r # convertImpl + +class ToInternalConverter input output | input -> output where + convertImpl :: input -> output + +instance ToInternalConverter Method String where + convertImpl = show + +instance ToInternalConverter Headers Headers where + convertImpl = identity + +instance ToInternalConverter RequestBody RequestBody where + convertImpl = identity + +instance ToInternalConverter RequestCredentials String where + convertImpl = RequestCredentials.toString + +instance ToInternalConverter RequestCache String where + convertImpl = RequestCache.toString + +instance ToInternalConverter RequestMode String where + convertImpl = RequestMode.toString + +instance ToInternalConverter Referrer String where + convertImpl = Referrer.toString + +instance ToInternalConverter ReferrerPolicy String where + convertImpl = ReferrerPolicy.toString + +instance ToInternalConverter Integrity String where + convertImpl = un Integrity + +instance ToInternalConverter Duplex String where + convertImpl = Duplex.toString diff --git a/src/Web/Fetch/RequestBody.js b/src/JS/Fetch/Fetch/RequestBody.js similarity index 100% rename from src/Web/Fetch/RequestBody.js rename to src/JS/Fetch/Fetch/RequestBody.js diff --git a/src/Web/Fetch/RequestBody.purs b/src/JS/Fetch/Fetch/RequestBody.purs similarity index 85% rename from src/Web/Fetch/RequestBody.purs rename to src/JS/Fetch/Fetch/RequestBody.purs index 7b937c6..14ece99 100644 --- a/src/Web/Fetch/RequestBody.purs +++ b/src/JS/Fetch/Fetch/RequestBody.purs @@ -1,4 +1,4 @@ -module Web.Fetch.RequestBody where +module JS.Fetch.RequestBody where import Data.ArrayBuffer.Types (ArrayBuffer, ArrayView, Uint8Array) import Web.Streams.ReadableStream (ReadableStream) @@ -13,4 +13,4 @@ foreign import fromString :: String -> RequestBody foreign import fromReadableStream :: ReadableStream Uint8Array -> RequestBody -foreign import empty :: RequestBody \ No newline at end of file +foreign import empty :: RequestBody diff --git a/src/Web/Fetch/RequestCache.purs b/src/JS/Fetch/Fetch/RequestCache.purs similarity index 92% rename from src/Web/Fetch/RequestCache.purs rename to src/JS/Fetch/Fetch/RequestCache.purs index 10d244a..e1a34b4 100644 --- a/src/Web/Fetch/RequestCache.purs +++ b/src/JS/Fetch/Fetch/RequestCache.purs @@ -1,4 +1,4 @@ -module Web.Fetch.RequestCache where +module JS.Fetch.RequestCache where import Data.Maybe (Maybe(..)) @@ -27,4 +27,4 @@ fromString = case _ of "no-cache" -> Just NoCache "force-cache" -> Just ForceCache "only-if-cached" -> Just OnlyIfCached - _ -> Nothing \ No newline at end of file + _ -> Nothing diff --git a/src/Web/Fetch/RequestCredentials.purs b/src/JS/Fetch/Fetch/RequestCredentials.purs similarity index 87% rename from src/Web/Fetch/RequestCredentials.purs rename to src/JS/Fetch/Fetch/RequestCredentials.purs index 8727295..9a41da7 100644 --- a/src/Web/Fetch/RequestCredentials.purs +++ b/src/JS/Fetch/Fetch/RequestCredentials.purs @@ -1,4 +1,4 @@ -module Web.Fetch.RequestCredentials where +module JS.Fetch.RequestCredentials where import Data.Maybe (Maybe(..)) @@ -18,4 +18,4 @@ fromString = case _ of "omit" -> Just Omit "same-origin" -> Just SameOrigin "include" -> Just Include - _ -> Nothing \ No newline at end of file + _ -> Nothing diff --git a/src/Web/Fetch/RequestMode.purs b/src/JS/Fetch/Fetch/RequestMode.purs similarity index 89% rename from src/Web/Fetch/RequestMode.purs rename to src/JS/Fetch/Fetch/RequestMode.purs index c173e1d..fbc4501 100644 --- a/src/Web/Fetch/RequestMode.purs +++ b/src/JS/Fetch/Fetch/RequestMode.purs @@ -1,4 +1,4 @@ -module Web.Fetch.RequestMode where +module JS.Fetch.RequestMode where import Data.Maybe (Maybe(..)) @@ -21,4 +21,4 @@ fromString = case _ of "no-cors" -> Just NoCors "same-origin" -> Just SameOrigin "navigate" -> Just Navigate - _ -> Nothing \ No newline at end of file + _ -> Nothing diff --git a/src/Web/Fetch/RequestRedirect.purs b/src/JS/Fetch/Fetch/RequestRedirect.purs similarity index 86% rename from src/Web/Fetch/RequestRedirect.purs rename to src/JS/Fetch/Fetch/RequestRedirect.purs index ee215db..561ab8b 100644 --- a/src/Web/Fetch/RequestRedirect.purs +++ b/src/JS/Fetch/Fetch/RequestRedirect.purs @@ -1,4 +1,4 @@ -module Web.Fetch.RequestRedirect where +module JS.Fetch.RequestRedirect where import Data.Maybe (Maybe(..)) @@ -18,4 +18,4 @@ fromString = case _ of "follow" -> Just Follow "error" -> Just Error "manual" -> Just Manual - _ -> Nothing \ No newline at end of file + _ -> Nothing diff --git a/src/Web/Fetch/Response.js b/src/JS/Fetch/Fetch/Response.js similarity index 89% rename from src/Web/Fetch/Response.js rename to src/JS/Fetch/Fetch/Response.js index 7c2db67..382921c 100644 --- a/src/Web/Fetch/Response.js +++ b/src/JS/Fetch/Fetch/Response.js @@ -45,3 +45,9 @@ export function text(resp) { return resp.text(); }; } + +export function json(resp) { + return function() { + return resp.json(); + }; +} diff --git a/src/Web/Fetch/Response.purs b/src/JS/Fetch/Fetch/Response.purs similarity index 66% rename from src/Web/Fetch/Response.purs rename to src/JS/Fetch/Fetch/Response.purs index 8f2c721..2213cfc 100644 --- a/src/Web/Fetch/Response.purs +++ b/src/JS/Fetch/Fetch/Response.purs @@ -1,9 +1,23 @@ -module Web.Fetch.Response where +module JS.Fetch.Response + ( Response + , arrayBuffer + , blob + , body + , headers + , json + , ok + , redirected + , status + , statusText + , text + , url + ) where import Data.ArrayBuffer.Types (ArrayBuffer, Uint8Array) import Effect (Effect) +import Foreign (Foreign) +import JS.Fetch.Headers (Headers) import Promise (Promise) -import Web.Fetch.Headers (Headers) import Web.File.Blob (Blob) import Web.Streams.ReadableStream (ReadableStream) @@ -27,4 +41,6 @@ foreign import arrayBuffer :: Response -> Effect (Promise ArrayBuffer) foreign import blob :: Response -> Effect (Promise Blob) -foreign import text :: Response -> Effect (Promise String) \ No newline at end of file +foreign import text :: Response -> Effect (Promise String) + +foreign import json :: Response -> Effect (Promise Foreign) diff --git a/src/Web/Fetch/Integrity.purs b/src/Web/Fetch/Integrity.purs deleted file mode 100644 index c9a4526..0000000 --- a/src/Web/Fetch/Integrity.purs +++ /dev/null @@ -1,10 +0,0 @@ -module Web.Fetch.Integrity where - -import Data.Newtype (class Newtype) -import Prelude (class Eq, class Ord) - -newtype Integrity = Integrity String - -derive instance newtypeIntegrity :: Newtype Integrity _ -derive newtype instance eqIntegrity :: Eq Integrity -derive newtype instance ordIntegrity :: Ord Integrity \ No newline at end of file diff --git a/src/Web/Fetch/Request.purs b/src/Web/Fetch/Request.purs deleted file mode 100644 index 35ff325..0000000 --- a/src/Web/Fetch/Request.purs +++ /dev/null @@ -1,191 +0,0 @@ -module Web.Fetch.Request - ( Request - , RequestOptions - , UnsafeRequestOptions - , new - , new' - , unsafeNew - , class BuildRequestOptions - , buildRequestOptions - , defaultOptions - , defaultUnsafeOptions - , class ConvertOptions - , convertOptions - , class ConvertOption - , convertOption - ) where - -import Prelude - -import Data.ArrayBuffer.Types (ArrayBuffer, ArrayView) -import Data.HTTP.Method (Method(..)) -import Data.Maybe (Maybe(..)) -import Data.Newtype (un) -import Data.Nullable (Nullable, toNullable) -import Data.Symbol (class IsSymbol) -import Data.Tuple (Tuple) -import Effect (Effect) -import Effect.Uncurried (EffectFn2, runEffectFn2) -import Foreign.Object (Object) -import Prim.Row as Row -import Prim.RowList (RowList) -import Prim.RowList as RowList -import Record (merge) -import Record.Builder as Record -import Type.Proxy (Proxy(..)) -import Type.Row.Homogeneous (class Homogeneous) -import Web.Fetch.Headers (Headers) -import Web.Fetch.Headers as Headers -import Web.Fetch.Integrity (Integrity(..)) -import Web.Fetch.Referrer (Referrer) -import Web.Fetch.Referrer as Referrer -import Web.Fetch.ReferrerPolicy (ReferrerPolicy) -import Web.Fetch.ReferrerPolicy as ReferrerPolicy -import Web.Fetch.RequestBody (RequestBody) -import Web.Fetch.RequestBody as RequestBody -import Web.Fetch.RequestCache (RequestCache) -import Web.Fetch.RequestCache as RequestCache -import Web.Fetch.RequestCredentials (RequestCredentials) -import Web.Fetch.RequestCredentials as RequestCredentials -import Web.Fetch.RequestMode (RequestMode) -import Web.Fetch.RequestMode as RequestMode - -foreign import data Request :: Type - -foreign import _unsafeNew :: forall r. EffectFn2 String { | r } Request - -type UnsafeRequestOptions = - ( method :: String - , headers :: Headers - , body :: RequestBody - , credentials :: String - , cache :: String - , mode :: String - , referrer :: Nullable String - , referrerPolicy :: String - , integrity :: String - ) - -type RequestOptions = - ( method :: Method - , headers :: Headers - , body :: RequestBody - , credentials :: RequestCredentials - , cache :: RequestCache - , mode :: RequestMode - , referrer :: Maybe Referrer - , referrerPolicy :: ReferrerPolicy - , integrity :: Integrity - ) - -defaultOptions :: { | RequestOptions } -defaultOptions = - { method: GET - , headers: Headers.empty - , body: RequestBody.empty - , credentials: RequestCredentials.SameOrigin - , cache: RequestCache.Default - , mode: RequestMode.Cors - , referrer: Nothing - , referrerPolicy: ReferrerPolicy.NoReferrer - , integrity: Integrity "" - } - -defaultUnsafeOptions :: { | UnsafeRequestOptions } -defaultUnsafeOptions = toUnsafeOptions defaultOptions - -toUnsafeOptions :: { | RequestOptions } -> { | UnsafeRequestOptions } -toUnsafeOptions opts = - { method: show opts.method - , headers: opts.headers - , body: opts.body - , credentials: RequestCredentials.toString opts.credentials - , cache: RequestCache.toString opts.cache - , mode: RequestMode.toString opts.mode - , referrer: toNullable $ Referrer.toString <$> opts.referrer - , referrerPolicy: ReferrerPolicy.toString opts.referrerPolicy - , integrity: un Integrity opts.integrity - } - -unsafeNew :: String -> { | UnsafeRequestOptions } -> Effect Request -unsafeNew = runEffectFn2 _unsafeNew - -new :: String -> { | RequestOptions } -> Effect Request -new url options = runEffectFn2 _unsafeNew url (toUnsafeOptions options) - -new' :: forall r. BuildRequestOptions { | r } => String -> { | r } -> Effect Request -new' url options = runEffectFn2 _unsafeNew url (toUnsafeOptions (buildRequestOptions options)) - -class BuildRequestOptions a where - buildRequestOptions :: a -> { | RequestOptions } - -instance buildRequestOptionsRecord - :: ( RowList.RowToList r rl - , ConvertOptions rl r r' - , Row.Union r' RequestOptions r'' - , Row.Nub r'' RequestOptions - ) - => BuildRequestOptions { | r } where - buildRequestOptions r = merge (Record.build (convertOptions (Proxy :: _ rl)) r) defaultOptions - -class ConvertOptions (rl :: RowList Type) (input :: Row Type) (output :: Row Type) | rl input -> output where - convertOptions :: forall rlproxy. rlproxy rl -> Record.Builder { | input } { | output } - -instance convertOptionsCons :: - ( ConvertOptions rest input' output - , ConvertOption field from to - , Row.Cons field from rx input - , Row.Cons field to rx input' - , IsSymbol field - ) => ConvertOptions (RowList.Cons field from rest) input output where - convertOptions _ = - convertOptions (Proxy :: _ rest) - <<< Record.modify (Proxy :: _ field) (convertOption (Proxy :: _ field)) - -instance convertOptionsNil :: ConvertOptions RowList.Nil r r where - convertOptions _ = identity - -class ConvertOption (field :: Symbol) from to | field -> to where - convertOption :: forall sproxy. sproxy field -> from -> to - -instance convertMethod :: ConvertOption "method" Method Method where - convertOption _ = identity - -instance convertBody :: ConvertOption "body" RequestBody RequestBody where - convertOption _ = identity - -instance convertBodyString :: ConvertOption "body" String RequestBody where - convertOption _ = RequestBody.fromString - -instance convertBodyBuffer :: ConvertOption "body" ArrayBuffer RequestBody where - convertOption _ = RequestBody.fromArrayBuffer - -instance convertBodyView :: ConvertOption "body" (ArrayView t) RequestBody where - convertOption _ = RequestBody.fromArrayView - -instance convertHeaders :: ConvertOption "headers" Headers Headers where - convertOption _ = identity - -instance convertHeadersRecord :: Homogeneous r String => ConvertOption "headers" { | r } Headers where - convertOption _ = Headers.fromRecord - -instance convertHeadersObject :: ConvertOption "headers" (Object String) Headers where - convertOption _ = Headers.fromObject - -instance convertHeadersArray :: ConvertOption "headers" (Array (Tuple String String)) Headers where - convertOption _ = Headers.fromFoldable - -instance convertCache :: ConvertOption "cache" RequestCache RequestCache where - convertOption _ = identity - -instance convertMode :: ConvertOption "mode" RequestMode RequestMode where - convertOption _ = identity - -instance convertReferrer :: ConvertOption "referrer" Referrer (Maybe Referrer) where - convertOption _ = Just - -instance convertReferrerPolicy :: ConvertOption "referrerPolicy" ReferrerPolicy ReferrerPolicy where - convertOption _ = identity - -instance convertIntegrity :: ConvertOption "integrity" Integrity Integrity where - convertOption _ = identity diff --git a/test.dhall b/test.dhall new file mode 100644 index 0000000..b49bd5f --- /dev/null +++ b/test.dhall @@ -0,0 +1,14 @@ +let conf = ./spago.dhall + +in conf + // { sources = conf.sources # [ "test/**/*.purs" ] + , dependencies = + conf.dependencies + # [ "aff" + , "aff-promise" + , "console" + , "debug" + , "effect" + , "unsafe-coerce" + ] + } diff --git a/test/Main.purs b/test/Main.purs new file mode 100644 index 0000000..a5e924a --- /dev/null +++ b/test/Main.purs @@ -0,0 +1,32 @@ +module Test.Main where + +import Prelude + +import Control.Promise as Promise +import Data.HTTP.Method (Method(..)) +import Debug (spy) +import Effect (Effect) +import Effect.Aff (launchAff_) +import Effect.Class (liftEffect) +import Effect.Class.Console (log) +import JS.Fetch as Fetch +import JS.Fetch.Duplex (Duplex(..)) +import JS.Fetch.Headers as Headers +import JS.Fetch.Request as Request +import JS.Fetch.RequestBody as RequestBody +import JS.Fetch.Response as Response +import Unsafe.Coerce (unsafeCoerce) + +main :: Effect Unit +main = launchAff_ do + let requestBody = """{"hello":"world"}""" + request <- liftEffect $ Request.new "http://httpbin.org/post" + { method: POST + , body: RequestBody.fromString requestBody + , headers: Headers.fromRecord { "Content-Type": "application/json" } + , duplex: Half + } + response <- Promise.toAffE $ unsafeCoerce $ Fetch.fetch request + responseBody <- Promise.toAffE $ unsafeCoerce $ Response.text response + log $ show $ Response.status response + log responseBody