diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..cb9c786 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,29 @@ +{ + "parserOptions": { + "ecmaVersion": 5 + }, + "extends": "eslint:recommended", + "env": { + "commonjs": true, + "browser": true + }, + "rules": { + "strict": [2, "global"], + "block-scoped-var": 2, + "consistent-return": 2, + "eqeqeq": [2, "smart"], + "guard-for-in": 2, + "no-caller": 2, + "no-extend-native": 2, + "no-loop-func": 2, + "no-new": 2, + "no-param-reassign": 2, + "no-return-assign": 2, + "no-unused-expressions": 2, + "no-use-before-define": 2, + "radix": [2, "always"], + "indent": [2, 2], + "quotes": [2, "double"], + "semi": [2, "always"] + } +} diff --git a/.gitignore b/.gitignore index 361cf52..709fd09 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -# Dependencies -.psci_modules -bower_components -node_modules - -# Generated files -.psci -output +/.* +!/.gitignore +!/.eslintrc.json +!/.travis.yml +package-lock.json +/bower_components/ +/node_modules/ +/output/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..37fead9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,21 @@ +language: node_js +dist: trusty +sudo: required +node_js: stable +env: + - PATH=$HOME/purescript:$PATH +install: + - TAG=$(wget -q -O - https://github.com/purescript/purescript/releases/latest --server-response --max-redirect 0 2>&1 | sed -n -e 's/.*Location:.*tag\///p') + - wget -O $HOME/purescript.tar.gz https://github.com/purescript/purescript/releases/download/$TAG/linux64.tar.gz + - tar -xvf $HOME/purescript.tar.gz -C $HOME/ + - chmod a+x $HOME/purescript + - npm install -g bower + - npm install +script: + - bower install + - npm run -s build +after_success: +- >- + test $TRAVIS_TAG && + echo $GITHUB_TOKEN | pulp login && + echo y | pulp publish --no-push diff --git a/README.md b/README.md index 4012bdb..23e6293 100644 --- a/README.md +++ b/README.md @@ -1 +1,16 @@ -# purescript-web-socket \ No newline at end of file +# purescript-web-socket + +[![Latest release](http://img.shields.io/github/release/purescript-web/purescript-web-socket.svg)](https://github.com/purescript-web/purescript-web-socket/releases) +[![Build status](https://travis-ci.org/purescript-web/purescript-web-socket.svg?branch=master)](https://travis-ci.org/purescript-web/purescript-web-socket) + +Type definitions and low level interface implementations for the W3C WebSocket API. + +## Installation + +``` +bower install purescript-web-socket +``` + +## Documentation + +Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-web-socket). diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..4cec920 --- /dev/null +++ b/bower.json @@ -0,0 +1,21 @@ +{ + "name": "purescript-web-socket", + "homepage": "https://github.com/purescript-web/purescript-web-socket", + "license": "MIT", + "repository": { + "type": "git", + "url": "git://github.com/purescript-web/purescript-web-socket.git" + }, + "ignore": [ + "**/.*", + "bower_components", + "node_modules", + "output", + "bower.json", + "package.json" + ], + "dependencies": { + "purescript-arraybuffer-types": "^2.0.0", + "purescript-web-file": "^1.0.0" + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..02e36ea --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "private": true, + "scripts": { + "clean": "rimraf output && rimraf .pulp-cache", + "build": "eslint src && pulp build -- --censor-lib --strict" + }, + "devDependencies": { + "eslint": "^4.19.1", + "pulp": "^12.2.0", + "purescript-psa": "^0.6.0", + "rimraf": "^2.6.2" + } +} diff --git a/src/Web/Socket/BinaryType.purs b/src/Web/Socket/BinaryType.purs new file mode 100644 index 0000000..b712b2c --- /dev/null +++ b/src/Web/Socket/BinaryType.purs @@ -0,0 +1,48 @@ +module Web.Socket.BinaryType where + +import Prelude +import Data.Enum (Cardinality(..), class BoundedEnum, defaultPred, defaultSucc, class Enum) +import Data.Maybe (Maybe(..)) + +data BinaryType + = Blob + | ArrayBuffer + +derive instance eqBinaryType :: Eq BinaryType +derive instance ordBinaryType :: Ord BinaryType + +instance boundedBinaryType :: Bounded BinaryType where + bottom = Blob + top = ArrayBuffer + +instance enumBinaryType :: Enum BinaryType where + succ = defaultSucc toEnumBinaryType fromEnumBinaryType + pred = defaultPred toEnumBinaryType fromEnumBinaryType + +instance boundedEnumBinaryType :: BoundedEnum BinaryType where + cardinality = Cardinality 2 + toEnum = toEnumBinaryType + fromEnum = fromEnumBinaryType + +instance showBinaryType :: Show BinaryType where + show Blob = "Blob" + show ArrayBuffer = "ArrayBuffer" + +toEnumBinaryType :: Int -> Maybe BinaryType +toEnumBinaryType = + case _ of + 0 -> Just Blob + 1 -> Just ArrayBuffer + _ -> Nothing + +fromEnumBinaryType :: BinaryType -> Int +fromEnumBinaryType = + case _ of + Blob -> 0 + ArrayBuffer -> 1 + +printBinaryType :: BinaryType -> String +printBinaryType = + case _ of + Blob -> "blob" + ArrayBuffer -> "arraybuffer" diff --git a/src/Web/Socket/Event/CloseEvent.js b/src/Web/Socket/Event/CloseEvent.js new file mode 100644 index 0000000..88734db --- /dev/null +++ b/src/Web/Socket/Event/CloseEvent.js @@ -0,0 +1,13 @@ +"use strict"; + +exports.code = function (e) { + return e.code; +}; + +exports.reason = function (e) { + return e.reason; +}; + +exports.wasClean = function (e) { + return e.wasClean; +}; diff --git a/src/Web/Socket/Event/CloseEvent.purs b/src/Web/Socket/Event/CloseEvent.purs new file mode 100644 index 0000000..5ca8879 --- /dev/null +++ b/src/Web/Socket/Event/CloseEvent.purs @@ -0,0 +1,20 @@ +module Web.Socket.Event.CloseEvent where + +import Data.Maybe (Maybe) +import Unsafe.Coerce (unsafeCoerce) +import Web.Event.Event (Event) +import Web.Internal.FFI (unsafeReadProtoTagged) + +foreign import data CloseEvent :: Type + +fromEvent :: Event -> Maybe CloseEvent +fromEvent = unsafeReadProtoTagged "CloseEvent" + +toEvent :: CloseEvent -> Event +toEvent = unsafeCoerce + +foreign import code :: CloseEvent -> Int + +foreign import reason :: CloseEvent -> String + +foreign import wasClean :: CloseEvent -> Boolean diff --git a/src/Web/Socket/Event/EventTypes.purs b/src/Web/Socket/Event/EventTypes.purs new file mode 100644 index 0000000..5aec5df --- /dev/null +++ b/src/Web/Socket/Event/EventTypes.purs @@ -0,0 +1,15 @@ +module Web.Socket.Event.EventTypes where + +import Web.Event.Event (EventType(..)) + +onOpen :: EventType +onOpen = EventType "open" + +onMessage :: EventType +onMessage = EventType "message" + +onError :: EventType +onError = EventType "error" + +onClose :: EventType +onClose = EventType "close" diff --git a/src/Web/Socket/Event/MessageEvent.js b/src/Web/Socket/Event/MessageEvent.js new file mode 100644 index 0000000..62472b6 --- /dev/null +++ b/src/Web/Socket/Event/MessageEvent.js @@ -0,0 +1,13 @@ +"use strict"; + +exports.data_ = function (e) { + return e.data; +}; + +exports.origin = function (e) { + return e.origin; +}; + +exports.lastEventId = function (e) { + return e.lastEventId; +}; diff --git a/src/Web/Socket/Event/MessageEvent.purs b/src/Web/Socket/Event/MessageEvent.purs new file mode 100644 index 0000000..bd7196e --- /dev/null +++ b/src/Web/Socket/Event/MessageEvent.purs @@ -0,0 +1,21 @@ +module Web.Socket.Event.MessageEvent where + +import Data.Maybe (Maybe) +import Foreign (Foreign) +import Unsafe.Coerce (unsafeCoerce) +import Web.Event.Event (Event) +import Web.Internal.FFI (unsafeReadProtoTagged) + +foreign import data MessageEvent :: Type + +fromEvent :: Event -> Maybe MessageEvent +fromEvent = unsafeReadProtoTagged "MessageEvent" + +toEvent :: MessageEvent -> Event +toEvent = unsafeCoerce + +foreign import data_ :: MessageEvent -> Foreign + +foreign import origin :: MessageEvent -> String + +foreign import lastEventId :: MessageEvent -> String diff --git a/src/Web/Socket/ReadyState.purs b/src/Web/Socket/ReadyState.purs new file mode 100644 index 0000000..0a23a48 --- /dev/null +++ b/src/Web/Socket/ReadyState.purs @@ -0,0 +1,50 @@ +module Web.Socket.ReadyState where + +import Prelude +import Data.Enum (Cardinality(..), class BoundedEnum, defaultPred, defaultSucc, class Enum) +import Data.Maybe (Maybe(..)) + +data ReadyState + = Connecting + | Open + | Closing + | Closed + +derive instance eqReadyState :: Eq ReadyState +derive instance ordReadyState :: Ord ReadyState + +instance boundedReadyState :: Bounded ReadyState where + bottom = Connecting + top = Closed + +instance enumReadyState :: Enum ReadyState where + succ = defaultSucc toEnumReadyState fromEnumReadyState + pred = defaultPred toEnumReadyState fromEnumReadyState + +instance boundedEnumReadyState :: BoundedEnum ReadyState where + cardinality = Cardinality 4 + toEnum = toEnumReadyState + fromEnum = fromEnumReadyState + +instance showReadyState :: Show ReadyState where + show Connecting = "Connecting" + show Open = "Open" + show Closing = "Closing" + show Closed = "Closed" + +toEnumReadyState :: Int -> Maybe ReadyState +toEnumReadyState = + case _ of + 0 -> Just Connecting + 1 -> Just Open + 2 -> Just Closing + 3 -> Just Closed + _ -> Nothing + +fromEnumReadyState :: ReadyState -> Int +fromEnumReadyState = + case _ of + Connecting -> 0 + Open -> 1 + Closing -> 2 + Closed -> 3 diff --git a/src/Web/Socket/WebSocket.js b/src/Web/Socket/WebSocket.js new file mode 100644 index 0000000..77405ab --- /dev/null +++ b/src/Web/Socket/WebSocket.js @@ -0,0 +1,67 @@ +"use strict"; + +exports.create = function (url) { + return function (protocols) { + return function () { + return new WebSocket(url, protocols); + }; + }; +}; + +exports.url = function (ws) { + return function () { + return ws.url; + }; +}; + +exports.readyStateImpl = function (ws) { + return function () { + return ws.readyState; + }; +}; + +exports.bufferedAmount = function (ws) { + return function () { + return ws.bufferedAmount; + }; +}; + +exports.extensions = function (ws) { + return function () { + return ws.extensions; + }; +}; + +exports.protocol = function (ws) { + return function () { + return ws.protocol; + }; +}; + +exports.close = function (ws) { + return function () { + return ws.close(); + }; +}; + +exports.getBinaryTypeImpl = function (ws) { + return function () { + return ws.binaryType; + }; +}; + +exports.setBinaryTypeImpl = function (ws) { + return function (bt) { + return function () { + ws.binaryType = bt; + }; + }; +}; + +exports.sendImpl = function (ws) { + return function (value) { + return function () { + ws.send(value); + }; + }; +}; diff --git a/src/Web/Socket/WebSocket.purs b/src/Web/Socket/WebSocket.purs new file mode 100644 index 0000000..0496363 --- /dev/null +++ b/src/Web/Socket/WebSocket.purs @@ -0,0 +1,91 @@ +module Web.Socket.WebSocket + ( WebSocket + , fromEventTarget + , toEventTarget + , Protocol(..) + , create + , url + , readyState + , bufferedAmount + , extensions + , protocol + , close + , getBinaryType + , setBinaryType + , sendString + , sendBlob + , sendArrayBuffer + , sendArrayBufferView + ) where + +import Prelude + +import Data.ArrayBuffer.Types (ArrayBuffer, ArrayView) +import Data.Maybe (Maybe, fromJust) +import Data.Newtype (class Newtype) +import Effect (Effect) +import Partial.Unsafe (unsafePartial) +import Unsafe.Coerce (unsafeCoerce) +import Web.Event.EventTarget (EventTarget) +import Web.File.Blob (Blob) +import Web.Internal.FFI (unsafeReadProtoTagged) +import Web.Socket.BinaryType (BinaryType(..), printBinaryType) +import Web.Socket.ReadyState (ReadyState, toEnumReadyState) + +foreign import data WebSocket :: Type + +fromEventTarget :: EventTarget -> Maybe WebSocket +fromEventTarget = unsafeReadProtoTagged "WebSocket" + +toEventTarget :: WebSocket -> EventTarget +toEventTarget = unsafeCoerce + +newtype Protocol = Protocol String + +derive newtype instance eqProtocol :: Eq Protocol +derive newtype instance ordProtocol :: Ord Protocol +derive instance newtypeProtocol :: Newtype Protocol _ + +foreign import create :: String -> Array Protocol -> Effect WebSocket + +foreign import url :: WebSocket -> Effect String + +foreign import readyStateImpl :: WebSocket -> Effect Int + +readyState :: WebSocket -> Effect ReadyState +readyState ws = do + rs <- readyStateImpl ws + pure $ unsafePartial $ fromJust $ toEnumReadyState rs + +foreign import bufferedAmount :: WebSocket -> Effect Number + +foreign import extensions :: WebSocket -> Effect String +foreign import protocol :: WebSocket -> Effect String + +foreign import close :: WebSocket -> Effect Unit + +foreign import getBinaryTypeImpl :: WebSocket -> Effect String +foreign import setBinaryTypeImpl :: WebSocket -> String -> Effect Unit + +getBinaryType :: WebSocket -> Effect BinaryType +getBinaryType ws = unsafePartial do + getBinaryTypeImpl ws <#> case _ of + "blob" -> Blob + "arraybuffer" -> ArrayBuffer + +setBinaryType :: WebSocket -> BinaryType -> Effect Unit +setBinaryType ws = setBinaryTypeImpl ws <<< printBinaryType + +sendString :: WebSocket -> String -> Effect Unit +sendString = sendImpl + +sendBlob :: WebSocket -> Blob -> Effect Unit +sendBlob = sendImpl + +sendArrayBuffer :: WebSocket -> ArrayBuffer -> Effect Unit +sendArrayBuffer = sendImpl + +sendArrayBufferView :: forall t. WebSocket -> ArrayView t -> Effect Unit +sendArrayBufferView = sendImpl + +foreign import sendImpl :: forall a. WebSocket -> a -> Effect Unit