Websockets with Snap won't bind in production - haskell

please help me with something:
I want to implement websockets-snap in production but they seem that won't take the address. I have the following code:
the js inside index.html file
function createChatSocket() {
if(window.location.host == '') {
/* Running on localhost */
return new WebSocket('ws://35.197.208.147/ws/console/');
} else {
/* Running in "production" */
return new WebSocket('wss://jaspervdj.be/websockets/example/chat/');
}
}
the window.location.host is not empty, it is the ip
the server.hs file
app :: Snap ()
app = Snap.route
[ ("", Snap.ifTop $ Snap.serveFile "./shorts/index.html")
, ("console", console)
]
....
....
....
--------------------------------------------------------------------------------
console :: Snap ()
console = do
state <- liftIO $ newMVar newServerState
WS.runWebSocketsSnap $ application state
No matter what i do, it will always return the websockets found on wss://jaspervdj.be/websockets/example/chat/ and not my server. What can i do?
Thanks

That's a JavaScript issue, not a Haskell one. The window.location.host will never be empty. Therefore, you always end up with the websocket to jaspervdj.be.

Related

Crash when trying to load a file using Node's repl library

I am getting a crash at the indicated point below when the code attempts to load a file. The file contents are read and displayed on the console. But when the line
app.ports.receiveData.send(data.toString());
is (tried to) execute, the code crashes. I've attached the error message below the code. The JS code here is used to run some Elm code "headless". The app.ports... function call is supposed send data back to the Elm app. (The Elm Code is further down).
JS CODE:
const repl = require('repl');
const fs = require('fs')
// Link to Elm code
var Elm = require('./main').Elm;
var main = Elm.Tool.init();
// Eval function for the repl
function eval(cmd, _, _, callback) {
main.ports.put.subscribe(
function putCallback (data) {
main.ports.put.unsubscribe(putCallback)
callback(null, data)
}
)
main.ports.get.send(cmd)
}
main.ports.sendFileName.subscribe(function(data) {
var path = data
// console.log(path)
fs.readFile(path, { encoding: 'utf8' }, (err, data) => {
if (err) {
console.error(err)
return
}
console.log(data.toString())
// Crash on next line !!!!
app.ports.receiveData.send(data.toString());
})
});
function myWriter(output) {
return output
}
console.log("\nType 'h' for help\n")
repl.start({ prompt: '> ', eval: eval, writer: myWriter});
Elm CODE
Here are the parts the Elm code that are relevant.
This code is called when the user wants to load a file.
loadFileCmd : String -> Cmd msg
loadFileCmd fileName =
sendFileName (E.string <| "./source/" ++ fileName)
These are the ports used to communicate with JS
port get : (String -> msg) -> Sub msg
port put : String -> Cmd msg
port sendFileName : E.Value -> Cmd msg
port receiveData : (E.Value -> msg) -> Sub msg
The get port listens for commands the user gives to the repl and gives these commands to Elm to process. The put port sends data that Elm computes to the repl.
The sendFileName port sends a file path to the repl. The receiveData port listens for the file contents. (But we crash before this can happen).
Here are the subscriptions:
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.batch [ Command.get Input, Command.receiveData ReceiveFileContents ]
ERROR MESSAGE:
repl.js:573
const lines = errStack.split(/(?<=\n)/);
^
TypeError: errStack.split is not a function
at Domain.debugDomainError (repl.js:573:30)
at Domain.emit (events.js:321:20)
at Domain.EventEmitter.emit (domain.js:485:12)
at Domain._errorHandler (domain.js:253:23)
at Object.<anonymous> (domain.js:156:29)
at process._fatalException (internal/process/execution.js:164:29)
From the comments, the answer was to replace the line
app.ports.receiveData.send(data.toString());
with
main.ports.receiveData.send(data.toString());
as the Elm app is named main, not app.

Svelte/Sapper - Encountered a build related bug with Luxon & Lodash

So I've just rolled out a new page (dashboard.svelte) on my prototype booking management app - I committed it along with a bunch of other changes and deployed to my Bitnami Node.js (Ubuntu 16.04) VPS.
Builds fine, but when loading any of the site in the browser, I get a bizarre error in the console which breaks execution of all other JS.
TypeError: Kn is not a function which ties back to the return Kn(t... section of a file called map.xxx.js:
var Kn, Xn = function (t, e) {
return function (n, r) {
if (null == n) return n;
if (!qe(n)) return t(n, r);
for (var i = n.length, o = e ? i : - 1, a = Object(n); (e ? o-- : ++o < i) && !1 !== r(a[o], o, a); );
return n
}
}(function (t, e) {
return t && Qn(t, e, Pe)
}); var tr = function (t, e) {
var n = - 1,
r = qe(t) ? Array(t.length) : [
];
return Kn(t, function (t, i, o) {
r[++n] = e(t, i, o)
}),
r
};
I spent several hours trying to track down the source of this bug (which to be honest is not very intuitive), and through trial and error tied it back to the aforementioned dashboard.svelte file (which works fine in npm run dev by the way)
https://gist.github.com/Bandit/58500ebc473e7d8d84bfb6ae16d41ea6
I've put all the information I can find about this in the above gist. I really hope someone can shed some light on the issue because I've had to rollback to an earlier commit for now.
FWIW I use DateTime via Luxon and several lodash methods elsewhere in the application without any issues, so it's not like this is the first time these packages have been built.
/EDIT1 Should note that if I rm src/routes/dashboard.svelte then npm run build everything works fine, so it's completely constrained to that one file...
/EDIT2 Commenting out the following lines in dashboard.svelte makes everything work fine:
// import merge from 'lodash/merge';
// const { session } = stores();
// $session.prefs = merge({}, $session.prefs);
But this makes no sense to me because this EXACT code exists in another page within my app:
import merge from 'lodash/merge';
import { onMount } from 'svelte';
import { hideAll } from 'tippy.js';
import { numberWithOrdinal } from './_helpers.js';
import { hasRole } from 'user.js';
const { session } = stores();
$session.prefs = merge({}, $session.prefs);
So it's something to do with the order of the imports?
/EDIT3 Regarding my comment below with the callstack, this is the line that triggers the issue (which is in a totally different file to dashboard.svelte and runs fine on the server):
$: if (dates && availableItems && availableBookings) populateGrid();
/EDIT4 So after extensive debugging we managed to pin it down to lodash.sortBy. Not sure why that is suddenly an issue as I've been using it for weeks without problem on another page within the app. Anyway I've removed all traces of it for now and all is well. For now (because I don't believe that is truly the issue)...
It's harder to debug because the JavaScript has been uglified.
In your rollup.config.js disable the terser plugin. (You can just comment it out)
Then re-run your build and it should give you non-mangled naming.
Another debugging tip is to edit the JS file map.xyz.js and add a debugger statement right before the line causing the error. Then re-run with devtools open. It will stop at that line and display the call stack. Look thru the call stack, it may shed light on where it's coming from.

Nodejs required variable undefined if script file not run directly?

I apologise for the phrasing of the question - it's a bit difficult to sum up as a question - please feel free to edit it if you can clarify. Also, as this quite a complex and long query - thank you to all those who are putting in the time to read through it!
I have 4 files (listed with directory tree from project root) as part of a project I'm building which aims to scrape blockchains and take advantage of multiple cores do get the job done:
./main.js
./scraper.js
./api/api.js
./api/litecoin_api.js
main.js
const { scraper } = require('./scraper.js')
const blockchainCli = process.env.BLOCKSCRAPECLI || 'litecoin-cli'
const client = (args) => {
// create child process which returns a promise which resolves after
// data has finished buffering from locally hosted node using cli
let child = spawn(`${blockchainCli} ${args.join(' ')}`, {
shell: true
})
// ... wrap command in a promise here, etc
}
const main = () => {
// count cores, spawn a worker per core using node cluster, add
// message handlers, then begin scraping blockchain with each core...
scraper(blockHeight)
}
main()
module.exports = {
client,
blockchainCli
}
scraper.js
const api = require('./api/api.js')
const scraper = async (blockHeight) => {
try {
let blockHash = await api.getBlockHashByHeight(blockHeight)
let block = await api.getBlock(blockHash)
// ... etc, scraper tested and working, writes to shared writeStream
}
module.exports = {
scraper
}
api.js
const { client, blockchainCli } = require('../main.js')
const litecoin = require('./litecoin_api')
let blockchain = undefined
if (blockchainCli === 'litecoin-cli' || blockchainCli === 'bitcoin-cli') {
blockchain = litecoin
}
// PROBLEM HERE: blockchainCli (and client) are both undefined if and
// only if running scraper from main.js (but not if running scraper
// from scraper.js)
const decodeRawTransaction = (txHash) => {
return client([blockchain.decodeRawTransaction, txHash])
}
const getBlock = (blockhash) => {
return client([blockchain.getBlock, blockhash])
}
const getBlockHashByHeight = (height) => {
return client([blockchain.getBlockHash, height])
}
const getInfo = () => {
return client([blockchain.getInfo])
}
const getRawTransaction = (txHash, verbose = true) => {
return client([blockchain.getRawTransaction, txHash, verbose])
}
module.exports = {
decodeRawTransaction,
getBlock,
getBlockHashByHeight,
getInfo,
getRawTransaction
}
So, I've taken out most the noise in the files which I don't think is necessary but it's open source so if you need more take a look here.
The problem is that, if I start the scraper from inside scraper.js by doing, say, something like this: scraper(1234567) it works like a charm and outputs the expected data to a csv file.
However if I start the scraper from inside the main.js file, I get this error:
Cannot read property 'getBlockHash' of undefined
at Object.getBlockHashByHeight (/home/grayedfox/github/blockscrape/api/api.js:19:29)
at scraper (/home/grayedfox/github/blockscrape/scraper.js:53:31)
at Worker.messageHandler (/home/grayedfox/github/blockscrape/main.js:81:5)
I don't know why, when launching the scraper from main.js, the blockchain is undefined. I thought it might be from the destructuring, but removing the curly braces from around the first line in the example main.js file doesn't change anything (same error).
Things are a bit messy at the moment (in the middle of developing this branch) - but the essential problem now is that it's not clear to me why the require would fail (cannot see variables inside main.js) if it's used in the following way:
main.js (execute scraper()) > scraper.js > api.js
But not fail (can see variables inside main.js) if it's run like this:
scraper.js (execute scraper()) > api.js
Thank you very much for your time!
You have a circular dependency between main and api, each requiring in the other. main requires api through scraper and api directly requires main. That causes things not to work.
You have to remove the circular dependency by putting common shared code into its own module that can be included by both, but doesn't include others that include it. It just needs better modularity.

How to create worker with elm 0.17

For elm 0.16, I just defined some ports
(that are just Signal of data) without main function and
used Signal.map to process data back and forth.
It is just for data processing called from nodejs (not from browser),
so I do not depend on Html module.
Now elm 0.17 has Cmd and Sub instead of Signal,
I cannot figure out how to do the same...
Can anybody give me an simplest example to do data
processing via ports exposed by worker initialization
with elm 0.17?
Here is my simplest example with Elm 0.16...
elm 0.16 code:
module Main where
import Signal
import String exposing (isEmpty, reverse)
-- input ports
port jsToElm : Signal String
-- output ports
port elmToJs : Signal String
port elmToJs
= Signal.map String.reverse jsToElm
javascript (es6) code:
/** main */
const Elm = loadElm('./index.js') // custom function to eval Elm code
const app = Elm.worker( Elm.Main, { jsToElm: ''})
app.ports.jsToElm.send('test')
app.ports.elmToJs.subscribe(( txt ) => {
console.log( txt )
})
Update
There is now a package that gives you the ability to create a worker. See lukewestby/worker
Original Answer
I was able to get a working example by creating an Html.App program. I can't find any way around the requirement of having a main function that results in a program that includes a view function, and I'm not alone in that confusion.
My example here is browser based, hopefully this fits back into your node-based version; I just haven't used the node version before.
Main.elm:
port module Main exposing (..)
import Json.Decode
import Json.Encode
import Html exposing (..)
import Html.App
import String exposing (isEmpty, reverse)
-- input ports
port jsToElm : (String -> msg) -> Sub msg
-- output ports
port elmToJs : String -> Cmd msg
type alias Flags = { jsToElm : String }
main =
Html.App.programWithFlags
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
type alias Model = { text : String }
init : Flags -> (Model, Cmd Msg)
init flags =
(Model flags.jsToElm, Cmd.none)
type Msg
= Reverse String
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Reverse str ->
let reversed = reverse str
in ({ model | text = reversed }, elmToJs reversed)
view : Model -> Html.Html Msg
view model =
text <| "text is: " ++ model.text
subscriptions : Model -> Sub Msg
subscriptions _ =
jsToElm Reverse
index.html:
<script type="text/javascript" src="Main.js"></script>
<script type="text/javascript">
var app = Elm.Main.fullscreen({
jsToElm: 'first'
});
window.setTimeout(function() {
app.ports.jsToElm.send('test');
}, 1);
app.ports.elmToJs.subscribe(function ( txt ) {
console.log( txt )
});
</script>
A couple takeaways:
As you can see, the simple mapping of Signals is gone and there is much more boilerplate for such a small example. The idea is that you set up your subscription to the port, then send a Cmd inside the update function when you want to send information back to Javascript.
This whole Cmd/Sub thing requires a Program and the only way to do that (that I could find) was to include [Html.App]. I have a hunch that in the future, the core Router type can be used to make a "headless" worker, but it seems like for now we're stuck with requiring the Html.App program and having to render a view.
You'll notice the setTimeout call when sending "test" to the port in javascript. I'm not sure why this kludge is necessary but others have seen it before as well
The upgrade guide goes into this, but I'll summarize here with references to your code:
Input Port:
-- 0.16
port jsToElm : Signal String
-- 0.17
port jsToElm : (String -> msg) -> Sub msg
Output Port:
-- 0.16
port elmToJs : Signal String
-- 0.17
port elmToJs : String -> Cmd msg
The new Elm guide explains exactly how to hook all of this up.

When starting a node JS server how do you detect if it was started with mimosa watch -s

i have a server.js file written in node.js
i can start the server with ... node server
or i can start it with ... mimosa watch -s
in the server.js file i want to do ...
if (this was started with mimosa) {
do something
} else {
do something else
}
how could i detect this? please no answers such as ... why do you want to do this?
currently i can only detect ...
var thisIsMimosa = false;
exports.startServer = function (config, callback) {
thisIsMimosa = true;
startServer("mimosa");
};
if (thisIsMimosa == false) {
startServer("node");
}
THE PROBLEM IS ... thisIsMimosa is not set in time and so the node startServer call is not made when "node server" is used to start the server.
If someone knows a variable which will differ in value if mimosa was used to start the server then I can rely on that instead.
Many thanks in advance.
The mimosa-config.js is a piece of node.js. It's not a .json file. So, you could, at the top of that file, do something like this:
process.env.IS_MIMOSA = true;
export.config = {
...
}
Then in your server.js you can check process.env.IS_MIMOSA. Mimosa reads in your config file long before it ever runs your server, so that variable should be ready to use.

Resources