diff --git a/README.md b/README.md
index 0743d9f..df44ff1 100644
--- a/README.md
+++ b/README.md
@@ -398,7 +398,7 @@ app.model({
}
],
effects: {
- 'sse:close': () => stream.close()
+ 'sse:close': () => stream.close(),
'app:error': (state, event) => console.error(`error: ${event.payload}`),
'app:print': (state, event) => console.log(`sse: ${event.payload}`)
}
diff --git a/examples/sse/client.js b/examples/sse/client.js
new file mode 100644
index 0000000..a386413
--- /dev/null
+++ b/examples/sse/client.js
@@ -0,0 +1,46 @@
+const choo = require('../../')
+
+const app = choo()
+app.model(createModel())
+app.router((route) => [
+ route('/', mainView)
+])
+
+const tree = app.start()
+document.body.appendChild(tree)
+
+function mainView (params, state, send) {
+ return choo.view`
+
${state.logger.msg}
+ `
+}
+
+function createModel () {
+ const stream = new window.EventSource('/sse')
+ return {
+ namespace: 'logger',
+ state: {
+ msg: ''
+ },
+ subscriptions: [
+ function (send) {
+ stream.onerror = (e) => {
+ send('logger:error', { payload: JSON.stringify(e) })
+ }
+ stream.onmessage = (e) => {
+ const msg = JSON.parse(e.data).message
+ send('logger:print', { payload: msg })
+ }
+ }
+ ],
+ reducers: {
+ 'print': (action, state) => {
+ return ({ msg: state.msg + ' ' + action.payload })
+ }
+ },
+ effects: {
+ close: () => stream.close(),
+ error: (action, state) => console.error(`error: ${action.payload}`)
+ }
+ }
+}
diff --git a/examples/sse/package.json b/examples/sse/package.json
new file mode 100644
index 0000000..4e94c61
--- /dev/null
+++ b/examples/sse/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "http",
+ "private": true,
+ "main": "client.js",
+ "scripts": {
+ "start": "NODE_ENV=development node server.js"
+ },
+ "dependencies": {
+ "bankai": "^2.0.2",
+ "browserify": "^13.0.1",
+ "server-router": "^2.1.0",
+ "sse": "0.0.6"
+ }
+}
diff --git a/examples/sse/server.js b/examples/sse/server.js
new file mode 100644
index 0000000..6854685
--- /dev/null
+++ b/examples/sse/server.js
@@ -0,0 +1,55 @@
+const serverRouter = require('server-router')
+const browserify = require('browserify')
+const bankai = require('bankai')
+const http = require('http')
+const SSE = require('sse')
+
+const clients = []
+const PORT = 8080
+
+const server = http.createServer(createRouter())
+server.listen(PORT, () => {
+ process.stdout.write(`listening on port ${PORT}\n`)
+ var sse = new SSE(server)
+
+ // code adapted from: https://github.com/markbrown4/server-sent-events-demo
+ sse.on('connection', (stream) => {
+ clients.push(stream)
+ console.log('Opened connection 🎉')
+
+ var json = JSON.stringify({ message: 'Gotcha' })
+ stream.send(json)
+ console.log('Sent: ' + json)
+
+ stream.on('close', () => {
+ clients.splice(clients.indexOf(stream), 1)
+ console.log('Closed connection 😱')
+ })
+ })
+})
+
+// emit data every 1.5 seconds
+setInterval(() => {
+ var json = JSON.stringify({ message: 'Hello hello!' })
+ clients.forEach((stream) => {
+ stream.send(json)
+ console.log('Sent: ' + json)
+ })
+}, 1500)
+
+function createRouter () {
+ const router = serverRouter('/404')
+
+ const html = bankai.html({ css: false })
+ router.on('/', (req, res) => html(req, res).pipe(res))
+
+ const js = bankai.js(browserify, require.resolve('./client.js'))
+ router.on('/bundle.js', (req, res) => js(req, res).pipe(res))
+
+ router.on('/404', (req, res) => {
+ res.statusCode = 404
+ res.end('{ "message": "the server is confused" }')
+ })
+
+ return router
+}
diff --git a/examples/sse/views/main.js b/examples/sse/views/main.js
new file mode 100644
index 0000000..4d0d435
--- /dev/null
+++ b/examples/sse/views/main.js
@@ -0,0 +1,14 @@
+const choo = require('../../../')
+
+module.exports = function (params, state, send) {
+ const error = state.app.error[0]
+ const title = state.api.title
+ return choo.view`
+
+ ${title}
+ Latest error: ${error}
+ send('api:good')}>OK!
+ send('api:bad')}>Naughty
+
+ `
+}
diff --git a/index.js b/index.js
index 6c6a0f2..42028cb 100644
--- a/index.js
+++ b/index.js
@@ -81,14 +81,17 @@ function choo () {
// subscriptions are loaded after sendAction() is called
// because they both need access to send() and can't
- // react to actions (read-only)
- _models.forEach(function (model) {
- if (model.subscriptions) {
- assert.ok(Array.isArray(model.subscriptions), 'subs must be an array')
- model.subscriptions.forEach(function (sub) {
- sub(send)
- })
- }
+ // react to actions (read-only) - also wait on DOM to
+ // be loaded
+ document.addEventListener('DOMContentLoaded', function () {
+ _models.forEach(function (model) {
+ if (model.subscriptions) {
+ assert.ok(Array.isArray(model.subscriptions), 'subs must be an arr')
+ model.subscriptions.forEach(function (sub) {
+ sub(send)
+ })
+ }
+ })
})
// If an id is provided, the application will rehydrate