diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..bcbc9a4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +language: node_js +sudo: false +node_js: + - 0.10 +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 bower pulp -g +- bower install +script: + - pulp build && pulp docs + - bower link + - cd example/ && bower link purescript-websocket-simple && bower install && pulp build + +after_success: + - >- + test $TRAVIS_TAG && + npm run psc-publish \ + | tail -n 1 \ + > output/documentation.json && + curl -X POST http://pursuit.purescript.org/packages \ + -d @output/documentation.json \ + -H 'Accept: application/json' \ + -H "Authorization: token ${GITHUB_TOKEN}" diff --git a/README.md b/README.md index 6fb13b9..be2590b 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,13 @@ # Simple Bindings to Websocket API for Purescript -## Example usage +[![Documentation](http://pursuit.purescript.org/packages/purescript-websocket-simple/badge)](http://pursuit.purescript.org/packages/purescript-websocket-simple) +[![Latest release](http://img.shields.io/bower/v/purescript-websocket-simple.svg)](https://github.com/zudov/purescript-websocket-simple/releases) +[![Build Status](https://travis-ci.org/zudov/purescript-websocket-simple.svg?branch=master)](https://travis-ci.org/zudov/purescript-websocket-simple) -```haskell -module Main where +## Example usage -import Prelude -import Control.Monad.Eff.Console -import WebSocket +See `example/src/Main.purs`. -main = do - ws <- mkWebSocket "ws://echo.websocket.org" - onMessage ws log - onOpen ws $ do - send ws "hello" - send ws "world" -``` +## Documentation +[Docs are on Pursuit](http://pursuit.purescript.org/packages/purescript-websocket-simple) diff --git a/bower.json b/bower.json index 8e3921b..ba35a72 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,10 @@ { - "name": "purescript-websocket-plain", - "version": "1.0.0", + "name": "purescript-websocket-simple", + "description": "A low-level wrapper around WebSocket API", + "authors": [ + "Konstantin Zudov " + ], + "license": "BSD3", "moduleType": [ "node" ], @@ -10,13 +14,18 @@ "bower_components", "output" ], - "repository": { - "type": "git", + "repository": { + "type": "git", "url": "git://github.com/zudov/purescript-websocket-simple.git" }, + "keywords": [ + "purescript" + ], "dependencies": { - "purescript-eff": "~0.1.0", - "purescript-functions": "~0.1.0" + "purescript-generics": "~0.6.2", + "purescript-eff": "~0.1.2", + "purescript-var": "~0.0.2", + "purescript-dom": "~0.2.8" }, "devDependencies": { "purescript-console": "~0.1.0" diff --git a/docs/WebSocket.md b/docs/WebSocket.md index 2c30ddf..5ad71fd 100644 --- a/docs/WebSocket.md +++ b/docs/WebSocket.md @@ -2,84 +2,196 @@ This module defines a simple low-level interface to the websockets API. -#### `WebSocket` +#### `WEBSOCKET` ``` purescript -data WebSocket :: ! +data WEBSOCKET :: ! ``` The effect associated with websocket connections. -#### `Socket` +#### `WebSocket` ``` purescript -data Socket :: * +data WebSocket :: * ``` -A reference to a websocket. +A reference to a WebSocket object. -#### `URI` +#### `newWebSocket` ``` purescript -type URI = String +newWebSocket :: forall eff. URL -> Array Protocol -> Eff (ws :: WEBSOCKET | eff) Connection ``` -A synonym for URI strings. +Initiate a websocket connection. -#### `Message` +#### `runMessageEvent` ``` purescript -type Message = String +runMessageEvent :: MessageEvent -> Message ``` -A synonym for message strings. +#### `Connection` -#### `mkWebSocket` +``` purescript +newtype Connection + = Connection { binaryType :: forall eff. Var (ws :: WEBSOCKET | eff) BinaryType, bufferedAmount :: forall eff. GettableVar (ws :: WEBSOCKET | eff) BufferedAmount, onclose :: forall eff handlerEff. SettableVar (ws :: WEBSOCKET | eff) (CloseEvent -> Eff handlerEff Unit), onerror :: forall eff handlerEff. SettableVar (ws :: WEBSOCKET | eff) (Event -> Eff handlerEff Unit), onmessage :: forall eff handlerEff. SettableVar (ws :: WEBSOCKET | eff) (MessageEvent -> Eff handlerEff Unit), onopen :: forall eff handlerEff. SettableVar (ws :: WEBSOCKET | eff) (Event -> Eff handlerEff Unit), protocol :: forall eff. Var (ws :: WEBSOCKET | eff) Protocol, readyState :: forall eff. GettableVar (ws :: WEBSOCKET | eff) ReadyState, url :: forall eff. GettableVar (ws :: WEBSOCKET | eff) URL, close :: forall eff. Maybe Code -> Maybe Reason -> Eff (ws :: WEBSOCKET | eff) Unit, send :: forall eff. Message -> Eff (ws :: WEBSOCKET | eff) Unit, socket :: forall eff. GettableVar (ws :: WEBSOCKET | eff) WebSocket } +``` + +#### `BinaryType` ``` purescript -mkWebSocket :: forall e. URI -> Eff (ws :: WebSocket | e) Socket +data BinaryType + = Blob + | ArrayBuffer ``` -Create a websocket object for a URI. +The type of binary data being transmitted by the connection. -#### `onMessage` +#### `BufferedAmount` ``` purescript -onMessage :: forall e a. Socket -> (Message -> Eff (ws :: WebSocket | e) a) -> Eff (ws :: WebSocket | e) Unit +newtype BufferedAmount ``` -Register a callback for incoming messages. +The number of bytes of data that have been buffered (queued but not yet transmitted) + +##### Instances +``` purescript +Generic BufferedAmount +Eq BufferedAmount +Ord BufferedAmount +``` -#### `onError` +#### `runBufferedAmount` ``` purescript -onError :: forall e a. Socket -> Eff (ws :: WebSocket | e) a -> Eff (ws :: WebSocket | e) Unit +runBufferedAmount :: BufferedAmount -> Int ``` -Register a callback for `error` events. +#### `Protocol` + +``` purescript +newtype Protocol + = Protocol String +``` -#### `onOpen` +A string indicating the name of the sub-protocol. +##### Instances ``` purescript -onOpen :: forall e a. Socket -> Eff (ws :: WebSocket | e) a -> Eff (ws :: WebSocket | e) Unit +Generic Protocol +Eq Protocol +Ord Protocol ``` -Register a callback for `open` events. +#### `runProtocol` + +``` purescript +runProtocol :: Protocol -> String +``` -#### `onClose` +#### `ReadyState` ``` purescript -onClose :: forall e a. Socket -> Eff (ws :: WebSocket | e) a -> Eff (ws :: WebSocket | e) Unit +data ReadyState + = Connecting + | Open + | Closing + | Closed ``` -Register a callback for `close` events. +State of the connection. + +##### Instances +``` purescript +Generic ReadyState +Eq ReadyState +Ord ReadyState +Show ReadyState +Bounded ReadyState +Enum ReadyState +``` + +#### `Code` + +``` purescript +newtype Code + = Code Int +``` + +A numeric value indicating the status code explaining why the connection is being closed. +See [the list of status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes). + +##### Instances +``` purescript +Generic Code +Eq Code +Ord Code +``` + +#### `runCode` + +``` purescript +runCode :: Code -> Int +``` -#### `send` +#### `Reason` ``` purescript -send :: forall e. Socket -> Message -> Eff (ws :: WebSocket | e) Unit +newtype Reason + = Reason String ``` -Send a message to a websocket. +A human-readable string explaining why the connection is closing. This +string must be no longer than 123 bytes of UTF-8 text (not characters). + +##### Instances +``` purescript +Generic Reason +Generic Reason +``` + +#### `runReason` + +``` purescript +runReason :: Reason -> String +``` + +#### `URL` + +``` purescript +newtype URL + = URL String +``` + +A synonym for URL strings. + +##### Instances +``` purescript +Generic URL +``` + +#### `runURL` + +``` purescript +runURL :: URL -> String +``` + +#### `Message` + +``` purescript +newtype Message + = Message String +``` + +A synonym for message strings. + +#### `runMessage` + +``` purescript +runMessage :: Message -> String +``` diff --git a/example/bower.json b/example/bower.json new file mode 100644 index 0000000..7bbb71e --- /dev/null +++ b/example/bower.json @@ -0,0 +1,17 @@ +{ + "name": "example", + "version": "1.0.0", + "moduleType": [ + "node" + ], + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "output" + ], + "dependencies": { + "purescript-console": "^0.1.0", + "purescript-websocket-simple": "*" + } +} diff --git a/example/index.html b/example/index.html new file mode 100644 index 0000000..11a9eab --- /dev/null +++ b/example/index.html @@ -0,0 +1,2 @@ + + diff --git a/example/src/Main.purs b/example/src/Main.purs new file mode 100644 index 0000000..f601841 --- /dev/null +++ b/example/src/Main.purs @@ -0,0 +1,41 @@ +module Main where + +import Prelude + +import Control.Bind ((=<<)) +import Control.Monad (when) +import Control.Monad.Eff.Var (($=), get) +import Control.Monad.Eff.Console (log) +import Control.Monad.Eff.Console.Unsafe (logAny) +import Data.Maybe (Maybe(..)) + +import WebSocket + +main = do + Connection socket <- newWebSocket (URL "ws://echo.websocket.org") [] + + socket.onopen $= \event -> do + logAny event + log "onopen: Connection opened" + + log <<< runURL =<< get socket.url + + log "onopen: Sending 'hello'" + socket.send (Message "hello") + + log "onopen: Sending 'goodbye'" + socket.send (Message "goodbye") + + socket.onmessage $= \event -> do + logAny event + let received = runMessage (runMessageEvent event) + + log $ "onmessage: Received '" ++ received ++ "'" + + when (received == "goodbye") do + log "onmessage: closing connection" + socket.close Nothing Nothing + + socket.onclose $= \event -> do + logAny event + log "onclose: Connection closed" diff --git a/src/WebSocket.js b/src/WebSocket.js index ba387c8..709f153 100644 --- a/src/WebSocket.js +++ b/src/WebSocket.js @@ -3,40 +3,56 @@ // module WebSocket -exports.mkWebSocket = function(url) { - return function() { - return new WebSocket(url); - } -} - -exports.onMessageImpl = function(socket, callback) { - return function() { - socket.onmessage = function(msg) { - callback(msg.data)(); - } - } -} - -exports.onErrorImpl = function(socket, callback) { - return function() { - socket.onerror = callback; - } -} - -exports.onCloseImpl = function(socket, callback) { - return function() { - socket.onclose = callback; - } +exports.specViolation = function(s) { + throw new Error(s); } -exports.onOpenImpl = function(socket, callback) { - return function() { - socket.onopen = callback; +exports.newWebSocketImpl = function(url, protocols) { + return function() { + var socket = new WebSocket(url, protocols); + var getSocketProp = function (prop) { + return function() { return socket[prop]; } } -} - -exports.sendImpl = function(socket, message) { - return function() { - socket.send(message); + var setSocketProp = function (prop) { + return function(v) { + return function() { + socket[prop] = v; + return {}; + } + } } + return { setBinaryType: setSocketProp("binaryType") + , getBinaryType: getSocketProp("binaryType") + , getBufferedAmount: getSocketProp("bufferedAmount") + , setOnclose: setSocketProp("onclose") + , getOnclose: getSocketProp("onclose") + , setOnerror: setSocketProp("onerror") + , getOnerror: getSocketProp("onerror") + , setOnmessage: setSocketProp("onmessage") + , getOnmessage: getSocketProp("onmessage") + , setOnopen: setSocketProp("onopen") + , getOnopen: getSocketProp("onopen") + , setProtocol: setSocketProp("protocol") + , getProtocol: getSocketProp("protocol") + , getReadyState: getSocketProp("readyState") + , getUrl: getSocketProp("url") + , closeImpl: + function(mCode) { + return function(mReason) { + return function() { + socket.close(mCode.value0, mReason.value0); + return {}; + } + } + } + , sendImpl: + function(message) { + return function() { + socket.send(message); + return {}; + } + } + , getSocket: function () { return socket } + }; + } } diff --git a/src/WebSocket.purs b/src/WebSocket.purs index 4fad712..90af7eb 100644 --- a/src/WebSocket.purs +++ b/src/WebSocket.purs @@ -1,86 +1,252 @@ -- | This module defines a simple low-level interface to the websockets API. module WebSocket - ( WebSocket() - , Socket() - , URI() - , Message() - , mkWebSocket - , onMessage - , onError - , onOpen - , onClose - , send + ( WEBSOCKET() + , WebSocket() + , newWebSocket + , Connection(..) + , URL(..) + , runURL + , Message(..) + , runMessage + , runMessageEvent + , Code(..) + , runCode + , Reason(..) + , runReason + , ReadyState(..) + , Protocol(..) + , runProtocol + , BufferedAmount() + , runBufferedAmount + , BinaryType(..) ) where import Prelude import Control.Monad.Eff +import Control.Monad.Eff.Var import Data.Function +import Data.Functor.Invariant +import Data.Functor.Contravariant +import Data.Nullable +import DOM.Event.EventTarget +import DOM.Event.Types +import Data.Maybe +import Data.Generic +import Data.Enum +import Unsafe.Coerce +import Data.Either +import Data.Foreign +import Data.Foreign.Index + +foreign import specViolation :: forall a. String -> a -- | The effect associated with websocket connections. -foreign import data WebSocket :: ! +foreign import data WEBSOCKET :: ! + +-- | A reference to a WebSocket object. +foreign import data WebSocket :: * + +-- | Initiate a websocket connection. +newWebSocket :: forall eff. URL -> Array Protocol -> Eff (ws :: WEBSOCKET | eff) Connection +newWebSocket url protocols = enhanceConnection <$> runFn2 newWebSocketImpl url protocols + +foreign import newWebSocketImpl :: forall eff. Fn2 URL + (Array Protocol) + (Eff (ws :: WEBSOCKET | eff) ConnectionImpl) + +runMessageEvent :: MessageEvent -> Message +runMessageEvent event = case prop "data" (toForeign event) of + Right x -> unsafeFromForeign x + Left _ -> specViolation "'data' missing from MessageEvent" + +type ConnectionImpl = + { setBinaryType :: forall eff. String -> Eff (ws :: WEBSOCKET | eff) Unit + , getBinaryType :: forall eff. Eff (ws :: WEBSOCKET | eff) String + , getBufferedAmount :: forall eff. Eff (ws :: WEBSOCKET | eff) BufferedAmount + , setOnclose :: forall eff handlerEff. EventListener handlerEff -> Eff (ws :: WEBSOCKET | eff) Unit + , setOnerror :: forall eff handlerEff. EventListener handlerEff -> Eff (ws :: WEBSOCKET | eff) Unit + , setOnmessage :: forall eff handlerEff. EventListener handlerEff -> Eff (ws :: WEBSOCKET | eff) Unit + , setOnopen :: forall eff handlerEff. EventListener handlerEff -> Eff (ws :: WEBSOCKET | eff) Unit + , setProtocol :: forall eff. Protocol -> Eff (ws :: WEBSOCKET | eff) Unit + , getProtocol :: forall eff. Eff (ws :: WEBSOCKET | eff) Protocol + , getReadyState :: forall eff. Eff (ws :: WEBSOCKET | eff) Int + , getUrl :: forall eff. Eff (ws :: WEBSOCKET | eff) URL + , closeImpl :: forall eff. Maybe Code -> Maybe Reason -> Eff (ws :: WEBSOCKET | eff) Unit + , sendImpl :: forall eff. Message -> Eff (ws :: WEBSOCKET | eff) Unit + , getSocket :: forall eff. Eff (ws :: WEBSOCKET | eff) WebSocket + } + +coerceEvent :: forall a. Event -> a +coerceEvent = unsafeCoerce + +enhanceConnection :: ConnectionImpl -> Connection +enhanceConnection c = Connection + { binaryType: imap toBinaryType fromBinaryType $ makeVar c.getBinaryType c.setBinaryType + , bufferedAmount: makeGettableVar c.getBufferedAmount + , onclose: cmap (eventListener <<< (`map` coerceEvent)) (makeSettableVar c.setOnclose) + , onerror: cmap (eventListener <<< (`map` coerceEvent)) (makeSettableVar c.setOnerror) + , onmessage: cmap (eventListener <<< (`map` coerceEvent)) (makeSettableVar c.setOnmessage) + , onopen: cmap (eventListener <<< (`map` coerceEvent)) (makeSettableVar c.setOnopen) + , protocol: makeVar c.getProtocol c.setProtocol + , readyState: unsafeReadyState <$> makeGettableVar c.getReadyState + , url: makeGettableVar c.getUrl + , close: c.closeImpl + , send: c.sendImpl + , socket: makeGettableVar c.getSocket + } + where + unsafeReadyState :: Int -> ReadyState + unsafeReadyState x = + fromMaybe (specViolation "readyState isn't in the range of valid constants") + (toEnum x) + + +-- | - `binaryType` -- The type of binary data being transmitted by the connection. +-- | - `bufferedAmount` -- The number of bytes of data that have been queued +-- using calls to `send` but not yet transmitted to the +-- network. This value does not reset to zero when the +-- connection is closed; if you keep calling `send`, +-- this will continue to climb. +-- | - `onclose` -- An event listener to be called when the `Connection`'s +-- | `readyState` changes to `Closed`. +-- | - `onerror` -- An event listener to be called when an error occurs. +-- | - `onmessage` -- An event listener to be called when a message is received +-- | from the server. +-- | - `onopen` -- An event listener to be called when the `Connection`'s +-- | readyState changes to `Open`; this indicates that the +-- | connection is ready to send and receive data. +-- | - `protocol` -- A string indicating the name of the sub-protocol the server selected. +-- | - `readyState` -- The current state of the connection. +-- | - `url` -- The URL as resolved by during construction. This is always an absolute URL. +-- | - `close` -- Closes the connection or connection attempt, if any. +-- | If the connection is already CLOSED, this method does nothing. +-- | If `Code` isn't specified a default value of 1000 (indicating +-- | a normal "transaction complete" closure) is assumed +-- | - `send` -- Transmits data to the server. +-- | - `socket` -- Reference to closured WebSocket object. +newtype Connection = Connection + { binaryType :: forall eff. Var (ws :: WEBSOCKET | eff) BinaryType + , bufferedAmount :: forall eff. GettableVar (ws :: WEBSOCKET | eff) BufferedAmount + , onclose :: forall eff handlerEff. SettableVar (ws :: WEBSOCKET | eff) (CloseEvent -> Eff handlerEff Unit) + , onerror :: forall eff handlerEff. SettableVar (ws :: WEBSOCKET | eff) (Event -> Eff handlerEff Unit) + , onmessage :: forall eff handlerEff. SettableVar (ws :: WEBSOCKET | eff) (MessageEvent -> Eff handlerEff Unit) + , onopen :: forall eff handlerEff. SettableVar (ws :: WEBSOCKET | eff) (Event -> Eff handlerEff Unit) + , protocol :: forall eff. Var (ws :: WEBSOCKET | eff) Protocol + , readyState :: forall eff. GettableVar (ws :: WEBSOCKET | eff) ReadyState + , url :: forall eff. GettableVar (ws :: WEBSOCKET | eff) URL + , close :: forall eff. Maybe Code -> Maybe Reason -> Eff (ws :: WEBSOCKET | eff) Unit + , send :: forall eff. Message -> Eff (ws :: WEBSOCKET | eff) Unit + , socket :: forall eff. GettableVar (ws :: WEBSOCKET | eff) WebSocket + } + +-- | The type of binary data being transmitted by the connection. +data BinaryType = Blob | ArrayBuffer + +toBinaryType :: String -> BinaryType +toBinaryType "blob" = Blob +toBinaryType "arraybuffer" = ArrayBuffer +toBinaryType s = specViolation "binaryType should be either 'blob' or 'arraybuffer'" + +fromBinaryType :: BinaryType -> String +fromBinaryType Blob = "blob" +fromBinaryType ArrayBuffer = "arraybuffer" + +-- | The number of bytes of data that have been buffered (queued but not yet transmitted) +newtype BufferedAmount = BufferedAmount Int + +runBufferedAmount :: BufferedAmount -> Int +runBufferedAmount (BufferedAmount a) = a + +derive instance genericBufferedAmount :: Generic BufferedAmount +instance eqBufferedAmount :: Eq BufferedAmount where + eq (BufferedAmount a) (BufferedAmount b) = eq a b +instance ordBufferedAmount :: Ord BufferedAmount where + compare (BufferedAmount a) (BufferedAmount b) = compare a b + +-- | A string indicating the name of the sub-protocol. +newtype Protocol = Protocol String + +runProtocol :: Protocol -> String +runProtocol (Protocol a) = a + +derive instance genericProtocol :: Generic Protocol +instance eqProtocol :: Eq Protocol where + eq (Protocol a) (Protocol b) = eq a b +instance ordProtocol :: Ord Protocol where + compare (Protocol a) (Protocol b) = compare a b + +-- | State of the connection. +data ReadyState = Connecting | Open | Closing | Closed + +derive instance genericReadyState :: Generic ReadyState + +instance eqReadyState :: Eq ReadyState where + eq = gEq + +instance ordReadyState :: Ord ReadyState where + compare = gCompare + +instance showReadyState :: Show ReadyState where + show = gShow + +instance boundedReadyState :: Bounded ReadyState where + bottom = Connecting + top = Closed + +instance enumReadyState :: Enum ReadyState where + cardinality = Cardinality 4 + toEnum = toEnumReadyState + fromEnum = fromEnumReadyState + succ = defaultSucc toEnumReadyState fromEnumReadyState + pred = defaultPred toEnumReadyState fromEnumReadyState + +toEnumReadyState :: Int -> Maybe ReadyState +toEnumReadyState 0 = Just Connecting +toEnumReadyState 1 = Just Open +toEnumReadyState 2 = Just Closing +toEnumReadyState 3 = Just Closed +toEnumReadyState _ = Nothing + +fromEnumReadyState :: ReadyState -> Int +fromEnumReadyState Connecting = 0 +fromEnumReadyState Open = 1 +fromEnumReadyState Closing = 2 +fromEnumReadyState Closed = 3 + +-- | A numeric value indicating the status code explaining why the connection is being closed. +-- | See [the list of status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes). +newtype Code = Code Int + +runCode :: Code -> Int +runCode (Code a) = a + +derive instance genericCode :: Generic Code +instance eqCode :: Eq Code where + eq (Code a) (Code b) = eq a b +instance ordCode :: Ord Code where + compare (Code a) (Code b) = compare a b + +-- | A human-readable string explaining why the connection is closing. This +-- | string must be no longer than 123 bytes of UTF-8 text (not characters). +newtype Reason = Reason String --- | A reference to a websocket. -foreign import data Socket :: * +runReason :: Reason -> String +runReason (Reason a) = a --- | A synonym for URI strings. -type URI = String +derive instance genericReason :: Generic Reason + +-- | A synonym for URL strings. +newtype URL = URL String + +runURL :: URL -> String +runURL (URL a) = a + +derive instance genericURL :: Generic URL -- | A synonym for message strings. -type Message = String - --- | Create a websocket object for a URI. -foreign import mkWebSocket :: forall e. URI -> Eff (ws :: WebSocket | e) Socket - -foreign import onMessageImpl :: forall e a. - Fn2 Socket - (Message -> Eff (ws :: WebSocket | e) a) - (Eff (ws :: WebSocket | e) Unit) - --- | Register a callback for incoming messages. -onMessage :: forall e a. - Socket -> (Message -> Eff (ws :: WebSocket | e) a) - -> Eff (ws :: WebSocket | e) Unit -onMessage socket callback = runFn2 onMessageImpl socket callback - -foreign import onErrorImpl :: forall e a. - Fn2 Socket - (Eff (ws :: WebSocket | e) a) - (Eff (ws :: WebSocket | e) Unit) - --- | Register a callback for `error` events. -onError :: forall e a. - Socket -> Eff (ws :: WebSocket | e) a - -> Eff (ws :: WebSocket | e) Unit -onError socket callback = runFn2 onErrorImpl socket callback - -foreign import onOpenImpl :: forall e a. - Fn2 Socket - (Eff (ws :: WebSocket | e) a) - (Eff (ws :: WebSocket | e) Unit) - --- | Register a callback for `open` events. -onOpen :: forall e a. - Socket -> Eff (ws :: WebSocket | e) a - -> Eff (ws :: WebSocket | e) Unit -onOpen socket callback = runFn2 onOpenImpl socket callback - -foreign import onCloseImpl :: forall e a. - Fn2 Socket - (Eff (ws :: WebSocket | e) a) - (Eff (ws :: WebSocket | e) Unit) - --- | Register a callback for `close` events. -onClose :: forall e a. - Socket -> Eff (ws :: WebSocket | e) a - -> Eff (ws :: WebSocket | e) Unit - -onClose socket callback = runFn2 onCloseImpl socket callback - -foreign import sendImpl :: forall e. - Fn2 Socket Message (Eff (ws :: WebSocket | e) Unit) - --- | Send a message to a websocket. -send :: forall e. - Socket -> Message -> Eff (ws :: WebSocket | e) Unit -send socket msg = runFn2 sendImpl socket msg +newtype Message = Message String +derive instance genericMessage :: Generic Reason + +runMessage :: Message -> String +runMessage (Message a) = a