├── test ├── results │ └── .gitkeep ├── fixtures │ ├── files │ │ ├── random.js │ │ ├── view.html │ │ ├── view.jade │ │ ├── template.html │ │ ├── foo │ │ │ └── template2.html │ │ ├── template.foo │ │ └── template.const │ ├── project │ │ ├── more │ │ │ └── a │ │ │ │ ├── a.js │ │ │ │ ├── a.css │ │ │ │ └── a.html │ │ ├── client │ │ │ ├── abc │ │ │ │ ├── index.js │ │ │ │ ├── index.a │ │ │ │ ├── style.css │ │ │ │ ├── abc.html │ │ │ │ ├── ss.html │ │ │ │ ├── expected-with-abc-constants.html │ │ │ │ └── expected.html │ │ │ ├── code │ │ │ │ ├── main.js │ │ │ │ ├── extras │ │ │ │ │ ├── e1.js │ │ │ │ │ └── e2.js │ │ │ │ ├── kickoff │ │ │ │ │ └── entry.js │ │ │ │ ├── modules │ │ │ │ │ └── message.coffee │ │ │ │ └── main │ │ │ │ │ └── demo.coffee │ │ │ ├── workers │ │ │ │ └── pi.js │ │ │ ├── templates │ │ │ │ ├── main.html │ │ │ │ ├── abc │ │ │ │ │ ├── 1.html │ │ │ │ │ └── 2.html │ │ │ │ ├── 1.html │ │ │ │ └── chat │ │ │ │ │ └── message.jade │ │ │ ├── views │ │ │ │ ├── main2.html │ │ │ │ ├── 1.jade │ │ │ │ └── main.jade │ │ │ ├── static │ │ │ │ ├── assets │ │ │ │ │ └── info.txt │ │ │ │ ├── favicon.ico │ │ │ │ └── images │ │ │ │ │ ├── logo.png │ │ │ │ │ └── admin.jpg │ │ │ └── css │ │ │ │ ├── main.styl │ │ │ │ ├── helpers.styl │ │ │ │ ├── demo.styl │ │ │ │ └── libs │ │ │ │ └── reset.css │ │ ├── README │ │ ├── .gitignore │ │ ├── console.js │ │ ├── node_modules │ │ │ ├── socketstream-addon │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ ├── object-assign │ │ │ │ ├── index.js │ │ │ │ ├── license │ │ │ │ ├── readme.md │ │ │ │ └── package.json │ │ │ └── mock-socket │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ ├── server │ │ │ ├── middleware │ │ │ │ └── example.js │ │ │ └── rpc │ │ │ │ └── demo.js │ │ ├── package.json │ │ └── app.js │ ├── readDirSync │ │ ├── index.js │ │ ├── dir1 │ │ │ └── .gitkeep │ │ ├── dir2 │ │ │ └── index.html │ │ └── dir3 │ │ │ ├── .gitkeep │ │ │ ├── dir3.1 │ │ │ └── .gitkeep │ │ │ ├── dir3.2 │ │ │ ├── .gitkeep │ │ │ └── dir3.2.1 │ │ │ │ └── test.js │ │ │ └── dir3.3 │ │ │ ├── test.css │ │ │ ├── dir3.3.1 │ │ │ └── .gitkeep │ │ │ ├── dir3.3.2 │ │ │ └── test.csv │ │ │ └── dir3.3.3 │ │ │ └── test.sh │ ├── stubs │ │ ├── formatter_const.js │ │ ├── template_engine.js │ │ └── formatter_html.js │ ├── helpers │ │ └── function.js │ ├── socketstream.js │ └── index.js ├── e2e │ └── readme.md ├── unit │ ├── session │ │ └── cookie.test.js │ ├── client │ │ ├── servePacked.test.js │ │ ├── clientIssue.test.js │ │ ├── http.test.js │ │ ├── abcClient.js │ │ ├── template_engines │ │ │ ├── ember.test.js │ │ │ └── default.test.js │ │ ├── formatters │ │ │ ├── css.test.js │ │ │ ├── html.test.js │ │ │ └── javascript.test.js │ │ ├── bundler │ │ │ └── custom.test.js │ │ └── asset.test.js │ ├── websocket │ │ └── transports │ │ │ └── engineio │ │ │ └── wrapper.test.js │ ├── test │ │ └── test-socketstream.test.js │ ├── utils │ │ ├── log.test.js │ │ ├── unique_set.test.js │ │ └── require.test.js │ ├── cli │ │ └── index.test.js │ ├── tasks │ │ └── index.test.js │ └── request │ │ └── index.test.js └── helpers │ ├── utils.js │ ├── logHook.js │ └── uncache.js ├── new_project ├── server │ ├── rpc │ │ ├── .gitkeep │ │ ├── demo.coffee │ │ └── demo.js │ └── middleware │ │ ├── .gitkeep │ │ ├── example.coffee │ │ └── example.js ├── client │ ├── templates │ │ ├── .gitkeep │ │ └── chat │ │ │ ├── message.jade │ │ │ └── message.html │ ├── code │ │ └── app │ │ │ ├── app.minimal.coffee │ │ │ ├── app.minimal.js │ │ │ ├── entry.coffee │ │ │ ├── entry.js │ │ │ ├── app.demo.coffee │ │ │ └── app.demo.js │ ├── static │ │ ├── favicon.ico │ │ └── images │ │ │ └── logo.png │ ├── css │ │ ├── app.minimal.styl │ │ ├── app.minimal.less │ │ ├── app.minimal.css │ │ ├── libs │ │ │ └── reset.css │ │ ├── app.demo.styl │ │ ├── app.demo.css │ │ └── app.demo.less │ └── views │ │ ├── app.minimal.jade │ │ ├── app.minimal.html │ │ ├── app.demo.jade │ │ └── app.demo.html ├── README.md ├── scm_ignore_file └── node_monitor_ignore_file ├── .npmignore ├── .hound.yml ├── docs ├── font │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff ├── partials │ ├── api │ │ ├── bundler.html │ │ ├── utils.html │ │ ├── ss.server.server.html │ │ ├── ss.html │ │ ├── events.html │ │ ├── ss.version.html │ │ ├── client.html │ │ ├── ss.env.html │ │ ├── ss.root.html │ │ ├── start.html │ │ ├── set.html │ │ ├── client.task.html │ │ ├── ss.add.html │ │ ├── client.define.html │ │ ├── client.formatters.formatters.html │ │ └── ss.client.client.html │ ├── demos │ │ └── index.html │ └── tutorials │ │ ├── http_middleware.html │ │ ├── hot_to_test.html │ │ ├── how_to_write_css.html │ │ ├── modules.html │ │ ├── client_side_development.html │ │ ├── url_scheme.html │ │ ├── start_targets.html │ │ ├── serving_http_resources.html │ │ ├── choosing_protocol.html │ │ ├── client_side_constants.html │ │ ├── loading_assets_on_demand.html │ │ └── web_workers.html └── css │ ├── animations.css │ └── prettify.css ├── src └── docs │ ├── tutorials │ ├── ko │ │ ├── loading_assets_on_demand.md │ │ └── live_reload.md │ ├── en │ │ ├── http_middleware.ngdoc │ │ ├── hot_to_test.ngdoc │ │ ├── how_to_write_css.ngdoc │ │ ├── modules.ngdoc │ │ ├── client_side_development.ngdoc │ │ ├── url_scheme.ngdoc │ │ ├── start_targets.ngdoc │ │ ├── serving_http_resources.ngdoc │ │ ├── choosing_protocol.ngdoc │ │ ├── how_to_write_a_formatter.ngdoc │ │ ├── live_reload.ngdoc │ │ ├── client_side_constants.ngdoc │ │ ├── loading_assets_on_demand.ngdoc │ │ └── web_workers.ngdoc │ └── index.ngdoc │ ├── site │ └── header.html │ └── demos │ └── index.ngdoc ├── index.js ├── .gitignore ├── lib ├── websocket │ ├── subscriptions.js │ ├── transports │ │ └── engineio │ │ │ └── .jshintrc │ ├── transport.js │ ├── event_dispatcher.js │ └── index.js ├── client │ ├── template_engines │ │ ├── ember.js │ │ ├── angular.js │ │ └── default.js │ ├── formatters │ │ ├── css.js │ │ ├── javascript.js │ │ ├── map.js │ │ ├── html.js │ │ ├── jade.js │ │ └── sass.js │ ├── view.js │ ├── bundler │ │ └── production.js │ └── formatters.js ├── publish │ ├── transports │ │ ├── internal.js │ │ └── redis.js │ └── transport.js ├── cli │ └── index.js ├── request │ ├── middleware │ │ ├── index.js │ │ └── internal.js │ ├── responders │ │ ├── events │ │ │ ├── index.js │ │ │ └── client.js │ │ └── rpc │ │ │ └── client.js │ └── index.js ├── tasks │ └── live_reload.js ├── session │ ├── store.js │ └── channels.js └── utils │ └── misc.js ├── .gitconfig ├── .travis.yml ├── .editorconfig ├── gulp.js ├── bin └── socketstream ├── .jshintignore ├── .javascript.json ├── express.js ├── misc └── changelog.tpl.md ├── LICENSE └── .jshintrc /test/results/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /new_project/server/rpc/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/files/random.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/files/view.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/files/view.jade: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/project/more/a/a.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /new_project/client/templates/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /new_project/server/middleware/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/project/more/a/a.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/readDirSync/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/readDirSync/dir1/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/readDirSync/dir2/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/readDirSync/dir3/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/readDirSync/dir3/dir3.1/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/readDirSync/dir3/dir3.2/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/readDirSync/dir3/dir3.3/test.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/project/client/abc/index.js: -------------------------------------------------------------------------------- 1 | // test 2 | -------------------------------------------------------------------------------- /test/fixtures/project/client/code/main.js: -------------------------------------------------------------------------------- 1 | // main 2 | -------------------------------------------------------------------------------- /new_project/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your new realtime app -------------------------------------------------------------------------------- /test/fixtures/files/template.html: -------------------------------------------------------------------------------- 1 |

{{ content }}

2 | -------------------------------------------------------------------------------- /test/fixtures/project/client/workers/pi.js: -------------------------------------------------------------------------------- 1 | // calc pi 2 | -------------------------------------------------------------------------------- /test/fixtures/readDirSync/dir3/dir3.2/dir3.2.1/test.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/readDirSync/dir3/dir3.3/dir3.3.1/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/readDirSync/dir3/dir3.3/dir3.3.2/test.csv: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/readDirSync/dir3/dir3.3/dir3.3.3/test.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/files/foo/template2.html: -------------------------------------------------------------------------------- 1 | {{ content }} 2 | -------------------------------------------------------------------------------- /test/fixtures/files/template.foo: -------------------------------------------------------------------------------- 1 | Random template content. 2 | -------------------------------------------------------------------------------- /test/fixtures/project/README: -------------------------------------------------------------------------------- 1 | # Welcome to your new realtime app -------------------------------------------------------------------------------- /test/fixtures/project/client/abc/index.a: -------------------------------------------------------------------------------- 1 | 2 | // index.a 3 | -------------------------------------------------------------------------------- /test/fixtures/project/client/code/extras/e1.js: -------------------------------------------------------------------------------- 1 | var e1 = 1; 2 | -------------------------------------------------------------------------------- /test/fixtures/project/client/code/extras/e2.js: -------------------------------------------------------------------------------- 1 | var e2 = 2; 2 | -------------------------------------------------------------------------------- /test/fixtures/files/template.const: -------------------------------------------------------------------------------- 1 | Some more random content. 2 | -------------------------------------------------------------------------------- /test/fixtures/project/client/templates/main.html: -------------------------------------------------------------------------------- 1 |
main
2 | -------------------------------------------------------------------------------- /test/fixtures/project/more/a/a.html: -------------------------------------------------------------------------------- 1 |
a
2 | -------------------------------------------------------------------------------- /test/fixtures/project/client/code/kickoff/entry.js: -------------------------------------------------------------------------------- 1 | var kick = 'off'; 2 | -------------------------------------------------------------------------------- /test/fixtures/project/client/templates/abc/1.html: -------------------------------------------------------------------------------- 1 |
abc 1
2 | -------------------------------------------------------------------------------- /test/fixtures/project/client/templates/abc/2.html: -------------------------------------------------------------------------------- 1 |
abc 2
2 | -------------------------------------------------------------------------------- /test/fixtures/project/client/views/main2.html: -------------------------------------------------------------------------------- 1 |
main view
2 | -------------------------------------------------------------------------------- /test/fixtures/project/client/templates/1.html: -------------------------------------------------------------------------------- 1 |
1
2 | -------------------------------------------------------------------------------- /test/fixtures/project/client/views/1.jade: -------------------------------------------------------------------------------- 1 | html 2 | body 3 | p one 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | docs 2 | .hound.yml 3 | .travis.yml 4 | .javascript.json 5 | TODO.md 6 | -------------------------------------------------------------------------------- /test/fixtures/project/client/abc/style.css: -------------------------------------------------------------------------------- 1 | /* style.css */ 2 | body {color:red;} 3 | -------------------------------------------------------------------------------- /test/fixtures/project/client/static/assets/info.txt: -------------------------------------------------------------------------------- 1 | saving assets here during tests 2 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | java_script: 2 | config_file: .javascript.json 3 | ignore_file: .jshintignore 4 | -------------------------------------------------------------------------------- /new_project/client/code/app/app.minimal.coffee: -------------------------------------------------------------------------------- 1 | # Client Code 2 | 3 | console.log('App Loaded') -------------------------------------------------------------------------------- /test/e2e/readme.md: -------------------------------------------------------------------------------- 1 | # e2e tests 2 | SocketStream will have end-to-end tests for development use. -------------------------------------------------------------------------------- /new_project/client/templates/chat/message.jade: -------------------------------------------------------------------------------- 1 | p 2 | span.time {{time}} 3 | span.message {{message}} -------------------------------------------------------------------------------- /test/fixtures/project/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | dump.rdb 3 | npm-debug.log 4 | tmp 5 | public/assets 6 | -------------------------------------------------------------------------------- /docs/font/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangnie/socketstream/develop/docs/font/FontAwesome.otf -------------------------------------------------------------------------------- /new_project/client/code/app/app.minimal.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Client Code 3 | 4 | console.log('App Loaded'); -------------------------------------------------------------------------------- /test/fixtures/project/client/templates/chat/message.jade: -------------------------------------------------------------------------------- 1 | p 2 | span.time {{time}} 3 | span.message {{message}} -------------------------------------------------------------------------------- /docs/font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangnie/socketstream/develop/docs/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangnie/socketstream/develop/docs/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangnie/socketstream/develop/docs/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /test/fixtures/project/client/abc/abc.html: -------------------------------------------------------------------------------- 1 | 2 | ABC 3 |

ABC

4 | 5 | -------------------------------------------------------------------------------- /new_project/scm_ignore_file: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | node_modules 4 | dump.rdb 5 | npm-debug.log 6 | tmp 7 | client/static/assets 8 | -------------------------------------------------------------------------------- /new_project/client/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangnie/socketstream/develop/new_project/client/static/favicon.ico -------------------------------------------------------------------------------- /new_project/client/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangnie/socketstream/develop/new_project/client/static/images/logo.png -------------------------------------------------------------------------------- /test/fixtures/project/client/abc/ss.html: -------------------------------------------------------------------------------- 1 | 2 | ABC 3 |

ABC

4 | 5 | -------------------------------------------------------------------------------- /new_project/client/templates/chat/message.html: -------------------------------------------------------------------------------- 1 |

2 | {{time}} 3 | {{message}} 4 |

5 | -------------------------------------------------------------------------------- /new_project/client/css/app.minimal.styl: -------------------------------------------------------------------------------- 1 | // Example Stylus file 2 | 3 | body, html 4 | min-height 100% 5 | 6 | body 7 | font normal 1em sans-serif 8 | -------------------------------------------------------------------------------- /src/docs/tutorials/ko/loading_assets_on_demand.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangnie/socketstream/develop/src/docs/tutorials/ko/loading_assets_on_demand.md -------------------------------------------------------------------------------- /test/fixtures/project/client/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangnie/socketstream/develop/test/fixtures/project/client/static/favicon.ico -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Initial entry point. Decides which directory of code to load 2 | 3 | // Load SocketStream core 4 | module.exports = require('./lib/socketstream.js'); -------------------------------------------------------------------------------- /test/fixtures/project/client/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangnie/socketstream/develop/test/fixtures/project/client/static/images/logo.png -------------------------------------------------------------------------------- /src/docs/site/header.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/project/client/static/images/admin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangnie/socketstream/develop/test/fixtures/project/client/static/images/admin.jpg -------------------------------------------------------------------------------- /new_project/client/css/app.minimal.less: -------------------------------------------------------------------------------- 1 | // Example Less file 2 | 3 | body, html { 4 | min-height: 100%; 5 | } 6 | 7 | body { 8 | font: normal 1em sans-serif; 9 | } 10 | -------------------------------------------------------------------------------- /new_project/client/css/app.minimal.css: -------------------------------------------------------------------------------- 1 | // Example CSS file 2 | 3 | body, 4 | html { 5 | min-height: 100%; 6 | } 7 | 8 | body { 9 | font: normal 1em sans-serif; 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | coverage 3 | /node_modules 4 | dump.rdb 5 | npm-debug.log 6 | *.tgz 7 | /docs.js 8 | test/fixtures/project/client/static/assets/abc 9 | .idea 10 | .settings 11 | -------------------------------------------------------------------------------- /test/fixtures/project/client/abc/expected-with-abc-constants.html: -------------------------------------------------------------------------------- 1 | 2 | ABC 3 |

ABC

5 | 6 | -------------------------------------------------------------------------------- /new_project/client/views/app.minimal.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang="en") 3 | head 4 | meta(charset="utf-8") 5 | != SocketStream 6 | title Welcome 7 | body 8 | p Welcome to your new realtime app! -------------------------------------------------------------------------------- /test/fixtures/project/client/abc/expected.html: -------------------------------------------------------------------------------- 1 | 2 | ABC 3 |

ABC

5 | 6 | -------------------------------------------------------------------------------- /test/unit/session/cookie.test.js: -------------------------------------------------------------------------------- 1 | var cookieParser = require('cookie-parser'), 2 | fixtures = require('../../fixtures'), 3 | ss = fixtures.socketstreamProject(); 4 | 5 | describe('session', function() { 6 | 7 | }); 8 | -------------------------------------------------------------------------------- /src/docs/demos/index.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name index 3 | @description 4 | 5 | # Demos 6 | 7 | - http://demo.socketstream.org/ - official project's demo 8 | - http://dashku.com - Realtime dashboards and widgets with HTML, CSS, and Javascript. -------------------------------------------------------------------------------- /new_project/client/views/app.minimal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome 7 | 8 | 9 |

Welcome to your new realtime app!

10 | 11 | -------------------------------------------------------------------------------- /new_project/server/middleware/example.coffee: -------------------------------------------------------------------------------- 1 | # Example request middleware 2 | 3 | # Only let a request through if the session has been authenticated 4 | exports.authenticated = -> 5 | (req, res, next) -> 6 | if req.session && req.session.userId? 7 | next() 8 | else 9 | res(false) 10 | -------------------------------------------------------------------------------- /new_project/node_monitor_ignore_file: -------------------------------------------------------------------------------- 1 | # Ignore file for Nodemon: https://github.com/remy/nodemon 2 | # Install with 'npm install -g nodemon' then start your app with 'nodemon app.js' 3 | # From then on, all changes you make to /server will cause your app to automatically restart 4 | 5 | /client/* 6 | ./README.md 7 | .git 8 | -------------------------------------------------------------------------------- /test/fixtures/project/client/code/modules/message.coffee: -------------------------------------------------------------------------------- 1 | # Example Message Module 2 | 3 | # Send a message to the server 4 | exports.send = (text, cb) -> 5 | if valid(text) 6 | ss.rpc('demo.sendMessage', text, cb) 7 | else 8 | cb(false) 9 | 10 | 11 | # Private 12 | 13 | valid = (text) -> 14 | text && text.length > 0 -------------------------------------------------------------------------------- /test/unit/client/servePacked.test.js: -------------------------------------------------------------------------------- 1 | describe('packed client',function() { 2 | 3 | it('should be served when option servePacked or packAssets set'); 4 | 5 | it('should serve JS ondemand within bounds defined'); 6 | 7 | it('should serve worker JS in definitions'); 8 | 9 | it('should include libs in client'); 10 | }); 11 | -------------------------------------------------------------------------------- /test/fixtures/project/console.js: -------------------------------------------------------------------------------- 1 | // Interactive console for testing sending of events etc 2 | // TODO: Find a way to give access to Websocket Message Responders here 3 | 4 | var repl = require('repl') 5 | , ss = require('socketstream'); 6 | 7 | ss.start(); 8 | 9 | var cmd = repl.start('SocketStream > '); 10 | 11 | cmd.context.ss = ss.api; -------------------------------------------------------------------------------- /test/fixtures/project/node_modules/socketstream-addon/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function(ss) { 2 | var strategy = { 3 | sessionMiddleware: sessionMiddleware, 4 | 5 | create: function() { 6 | return {}; 7 | } 8 | }; 9 | ss.session.setStrategy(strategy); 10 | 11 | function sessionMiddleware(req,res,next) { 12 | return next(); 13 | } 14 | 15 | }; -------------------------------------------------------------------------------- /test/fixtures/stubs/formatter_const.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Plain HTML Formatter 3 | 4 | var fs; 5 | 6 | fs = require('fs'); 7 | 8 | exports.init = function() { 9 | return { 10 | extensions: ['const'], 11 | assetType: 'html', 12 | contentType: 'text/html', 13 | compile: function(path, options, cb) { 14 | return cb('CONST'); 15 | } 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /lib/websocket/subscriptions.js: -------------------------------------------------------------------------------- 1 | // Websocket ID subscriptions 2 | // -------------------------- 3 | // Stores a list of which socket IDs are subscribed to which users or channels 4 | // and delivers events accordingly 5 | 'use strict'; 6 | 7 | var UniqueSet = require('../utils/unique_set').UniqueSet; 8 | 9 | module.exports = { 10 | user: new UniqueSet, 11 | channel: new UniqueSet 12 | }; 13 | -------------------------------------------------------------------------------- /new_project/server/middleware/example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Example request middleware 3 | 4 | // Only let a request through if the session has been authenticated 5 | exports.authenticated = function() { 6 | return function(req, res, next) { 7 | if (req.session && (req.session.userId != null)) { 8 | return next(); 9 | } else { 10 | return res(false); 11 | } 12 | }; 13 | }; -------------------------------------------------------------------------------- /.gitconfig: -------------------------------------------------------------------------------- 1 | [color] 2 | branch = auto 3 | diff = auto 4 | status = auto 5 | [color "branch"] 6 | current = yellow reverse 7 | local = yellow 8 | remote = green 9 | [color "diff"] 10 | meta = yellow bold 11 | frag = magenta bold 12 | old = red bold 13 | new = green bold 14 | [color "status"] 15 | added = yellow 16 | changed = green 17 | untracked = cyan 18 | -------------------------------------------------------------------------------- /test/fixtures/project/server/middleware/example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Example request middleware 3 | 4 | // Only let a request through if the session has been authenticated 5 | exports.authenticated = function() { 6 | return function(req, res, next) { 7 | if (req.session && (req.session.userId != null)) { 8 | return next(); 9 | } else { 10 | return res(false); 11 | } 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.12 4 | - 4 5 | - 6 6 | 7 | cache: 8 | directories: 9 | - node_modules 10 | 11 | before_install: 12 | - npm install --upgrade npm -g 13 | 14 | script: 15 | - npm run cover-test 16 | 17 | notifications: 18 | webhooks: 19 | urls: 20 | - https://webhooks.gitter.im/e/7937aa9d2e9d767d0421 21 | on_success: always 22 | on_failure: always 23 | -------------------------------------------------------------------------------- /test/fixtures/stubs/template_engine.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.init = function(name) { 4 | return { 5 | name: name, 6 | prefix: function() { 7 | return '<' + this.name + '::prefix>'; 8 | }, 9 | suffix: function() { 10 | return '<' + this.name + '::suffix>'; 11 | }, 12 | process: function(template, path, id) { 13 | return '[' + id + '::' + template + ']'; 14 | } 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /lib/client/template_engines/ember.js: -------------------------------------------------------------------------------- 1 | // Produces templates for Ember.js 2 | // Note Ember compiles these Handlebars templates one time only when you call Em.Application.create(); 3 | 'use strict'; 4 | 5 | exports.init = function() { 6 | return { 7 | name: 'Ember.js', 8 | process: function(template, path, id) { 9 | return ''; 10 | } 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /test/fixtures/stubs/formatter_html.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Plain HTML Formatter 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var fs; 8 | 9 | fs = require('fs'); 10 | 11 | exports.init = function() { 12 | return { 13 | extensions: ['html'], 14 | assetType: 'html', 15 | contentType: 'text/html', 16 | compile: function(path, options, cb) { 17 | var input; 18 | input = fs.readFileSync(path, 'utf8'); 19 | return cb(input); 20 | } 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /test/fixtures/project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my_realtime_app", 3 | "description": "An awesome real time application", 4 | "version": "0.0.1", 5 | "author": "Me ", 6 | "private": true, 7 | "engines": { "node": ">= 0.6.0" }, 8 | "dependencies": { 9 | "socketstream-addon": "*", 10 | "object-assign": "*", 11 | "ss-coffee": "0.1.x", 12 | "ss-stylus": "0.1.x", 13 | "ss-jade": "0.1.x", 14 | "ss-hogan": "0.1.x" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/http_middleware.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name HTTP Middleware 3 | 4 | @description 5 | # HTTP Middleware 6 | 7 | SocketStream no longer provides a stack of Connect HTTP middleware which is used internally to serve single-page clients, asset files and static files (e.g. images) in `client/static`. 8 | 9 | Session handling is done in add-ons like `socketstream-cookie-session`. 10 | 11 | For additional middleware follow the examples for Connect or ExpressJS. 12 | -------------------------------------------------------------------------------- /test/fixtures/helpers/function.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var toArray; 4 | 5 | toArray = function(enumerable) { 6 | return Array.prototype.slice.call(enumerable); 7 | }; 8 | 9 | Function.prototype.curry = function() { 10 | var args, __method; 11 | if (arguments.length < 1) { 12 | return this; 13 | } 14 | __method = this; 15 | args = toArray(arguments); 16 | return function() { 17 | return __method.apply(this, args.concat(toArray(arguments))); 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/client/template_engines/angular.js: -------------------------------------------------------------------------------- 1 | // Produces inline templates for Angular.js 2 | // Use them in conjunction with `ng-view`. Note the `.html` at the end of each template name which is automatically appended 3 | 4 | 'use strict'; 5 | 6 | exports.init = function() { 7 | return { 8 | name: 'Angular.js', 9 | process: function(template, path, id) { 10 | return ''; 11 | } 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /lib/publish/transports/internal.js: -------------------------------------------------------------------------------- 1 | // Publish Event - Internal EventEmitter Transport 2 | 'use strict'; 3 | 4 | var EventEmitter2, emitter; 5 | 6 | EventEmitter2 = require('eventemitter2').EventEmitter2; 7 | 8 | emitter = new EventEmitter2(); 9 | 10 | module.exports = function() { 11 | return { 12 | listen: function(cb) { 13 | return emitter.on('event', cb); 14 | }, 15 | send: function(obj) { 16 | return emitter.emit('event', obj); 17 | } 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/client/formatters/css.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | 5 | /** 6 | * Plain CSS Formatter 7 | */ 8 | module.exports = function(ss) { 9 | return { 10 | extensions: ['css'], 11 | assetType: 'css', 12 | contentType: 'text/css', 13 | compile: function (path, options, cb) { 14 | ss.log.trace('Compiling plain CSS',path,options); 15 | //TODO if no file, return error object 16 | return cb(fs.readFileSync(path, 'utf8')); 17 | } 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /lib/websocket/transports/engineio/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "eqeqeq": true, 3 | "indent": 2, 4 | "node": true, 5 | "predef": [ 6 | "document", 7 | "eio", 8 | "unescape", 9 | "__dirname", 10 | "after", 11 | "afterEach", 12 | "before", 13 | "beforeEach", 14 | "console", 15 | "describe", 16 | "it", 17 | "module", 18 | "process", 19 | "require", 20 | "setInterval" 21 | ], 22 | "strict": true, 23 | "trailing": true, 24 | "undef": true, 25 | "unused": true 26 | } 27 | -------------------------------------------------------------------------------- /lib/client/formatters/javascript.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | 5 | /** 6 | * Javascript formatter 7 | */ 8 | module.exports = function(ss) { 9 | return { 10 | extensions: ['js'], 11 | assetType: 'js', 12 | contentType: 'text/javascript; charset=utf-8', 13 | compile: function(path, options, cb) { 14 | ss.log.trace('Compiling plain JS',path,options); 15 | //TODO if no file, return error object 16 | return cb(fs.readFileSync(path, 'utf8')); 17 | } 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /test/fixtures/project/client/css/main.styl: -------------------------------------------------------------------------------- 1 | // Example Stylus file 2 | 3 | @import 'helpers' 4 | @import 'demo' 5 | 6 | // Main 7 | 8 | body, html 9 | height 100% 10 | 11 | body 12 | font normal 1em sans-serif 13 | vertical-gradient #eee, #fff 14 | text-align center 15 | 16 | p 17 | margin-bottom 1em 18 | 19 | a 20 | color #000a68 21 | 22 | h1 23 | font-size 1.5em 24 | font-weight normal 25 | margin 1.5em 26 | color #333 27 | text-shadow 1px 1px 2px white 28 | 29 | #content 30 | padding 50px 31 | 32 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | max_line_length = 150 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /new_project/client/code/app/entry.coffee: -------------------------------------------------------------------------------- 1 | # This file automatically gets called first by SocketStream and must always exist 2 | 3 | # Make 'ss' available to all modules and the browser console 4 | window.ss = require('socketstream') 5 | 6 | ss.server.on 'disconnect', -> 7 | console.log('Connection down :-(') 8 | 9 | ss.server.on 'reconnect', -> 10 | console.log('Connection back up :-)') 11 | 12 | ss.server.on 'ready', -> 13 | 14 | # Wait for the DOM to finish loading 15 | jQuery -> 16 | 17 | # Load app 18 | require('/app') 19 | -------------------------------------------------------------------------------- /gulp.js: -------------------------------------------------------------------------------- 1 | // Entry point for gulpfile.js 2 | 3 | // Load SocketStream core 4 | var ss = module.exports = require('./lib/socketstream.js'), 5 | gulp = ss.api.require('gulp'); 6 | 7 | // gulp is used as the Orchestrator 8 | ss.tasks.use(gulp); 9 | 10 | // Your app.js should be required from the gulpfile.js 11 | // .start() will normally be called from app.js, and will not run tasks. 12 | ss.start = function() {}; 13 | 14 | // default tasks are defined 15 | ss.tasks.defaults(); 16 | 17 | // don't load API yet as the app.js still needs to be loaded 18 | -------------------------------------------------------------------------------- /lib/client/template_engines/default.js: -------------------------------------------------------------------------------- 1 | // Default Engine produces standard '; 11 | } 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /test/unit/client/clientIssue.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'), 4 | fs = require('fs'), 5 | ss = require( '../../../lib/socketstream'), 6 | //options = ss.client.options, 7 | fixtures = require('../../fixtures'); 8 | 9 | 10 | describe('client asset manager index', function () { 11 | 12 | ss.root = ss.api.root = fixtures.project; 13 | 14 | it('should show exception in browser when serveDebugInfo is set'); 15 | 16 | it('should render errors in template engine calls depending on serveDebugInfo in client options'); 17 | }); 18 | -------------------------------------------------------------------------------- /docs/partials/api/bundler.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

bundler 2 |
3 |
4 |

5 |

Description

6 |

Bundlers included.

7 |
8 |
9 | -------------------------------------------------------------------------------- /lib/client/formatters/map.js: -------------------------------------------------------------------------------- 1 | // JS Minmap formatter 2 | // 3 | // courtesy of Waxolunist (gh) 4 | // 5 | 'use strict'; 6 | 7 | 8 | 9 | // Dependencies 10 | // 11 | var fs = require('fs'); 12 | 13 | module.exports = function(ss) { 14 | 15 | return { 16 | extensions: ['map'], 17 | assetType: 'js', 18 | contentType: 'application/json', 19 | compile: function(path, options, cb) { 20 | ss.log.trace('Compiling plain MAP',path,options); 21 | //TODO if no file, return error object 22 | return cb(fs.readFileSync(path, 'utf8')); 23 | } 24 | }; 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /bin/socketstream: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var ss = require('../') 4 | , program = require('commander'); 5 | 6 | program 7 | .usage('new ') 8 | .version(ss.version) 9 | .option('-m, --minimal', 'minimal install (no chat demo)') 10 | .option('-c, --coffee', 'use CoffeeScript') 11 | .option('-j, --jade', 'use Jade for Views') 12 | .option('-s, --stylus', 'use Stylus for CSS') 13 | .option('-l, --less', 'use Less for CSS') 14 | .option('-r, --repl', 'include Console Server / REPL') 15 | .parse(process.argv); 16 | 17 | require(__dirname + '/../lib/cli').process(program); 18 | -------------------------------------------------------------------------------- /docs/partials/demos/index.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

Demos

6 | 10 |
11 | -------------------------------------------------------------------------------- /docs/partials/api/utils.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

utils 2 |
3 |
4 |

5 |

Description

6 |

Contains utils modules for working with file system and some additional helpers

7 |
8 |
9 | -------------------------------------------------------------------------------- /test/fixtures/project/client/css/helpers.styl: -------------------------------------------------------------------------------- 1 | // Helper functions 2 | 3 | vendor(prop, args) 4 | -webkit-{prop} args 5 | -moz-{prop} args 6 | {prop} args 7 | 8 | round(n) 9 | vendor border-radius, n 10 | 11 | round-corner(tb, lr, n) 12 | border-{tb}\-{lr}-radius n 13 | -moz-border-radius-{tb}{lr} n 14 | 15 | box-shadow() 16 | vendor box-shadow, arguments 17 | 18 | opacity(n) 19 | opacity n 20 | -moz-opacity n 21 | 22 | vertical-gradient(from, to) 23 | background-color to 24 | background-image -webkit-gradient(linear, left top, left bottom, from(from), to(to)) 25 | background-image -moz-linear-gradient(top, from, to) 26 | -------------------------------------------------------------------------------- /new_project/client/code/app/entry.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // This file automatically gets called first by SocketStream and must always exist 3 | 4 | // Make 'ss' available to all modules and the browser console 5 | window.ss = require('socketstream'); 6 | 7 | ss.server.on('disconnect', function(){ 8 | console.log('Connection down :-('); 9 | }); 10 | 11 | ss.server.on('reconnect', function(){ 12 | console.log('Connection back up :-)'); 13 | }); 14 | 15 | ss.server.on('ready', function(){ 16 | 17 | // Wait for the DOM to finish loading 18 | jQuery(function(){ 19 | 20 | // Load app 21 | require('/app'); 22 | 23 | }); 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /test/fixtures/socketstream.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ss = module.exports = require('../../lib/socketstream'); 4 | ss.root = ss.api.root = require('./index').project; 5 | 6 | var apiKeys = Object.keys(ss.api); 7 | 8 | ss.reset = function() { 9 | for(var key in ss.api) { 10 | if (apiKeys.indexOf(key) >= 0) { 11 | delete ss.api[key]; 12 | } 13 | } 14 | ss.client.reset(); 15 | }; 16 | 17 | ss.client.reset = function() { 18 | ss.client.unload(); 19 | ss.client.forget(); 20 | ss.client.init(); 21 | ss.tasks.unload(); 22 | ss.tasks.forget(); 23 | }; 24 | 25 | ss.tasks.reset = function() { 26 | ss.tasks.forget(); 27 | }; 28 | -------------------------------------------------------------------------------- /new_project/client/views/app.demo.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang="en") 3 | head 4 | meta(charset="utf-8") 5 | != SocketStream 6 | title Welcome 7 | body 8 | #content 9 | a(href='https://github.com/socketstream/socketstream') 10 | img(src='/images/logo.png', alt='SocketStream Logo', width=160, height=160) 11 | h1 Welcome to your new realtime app! 12 | 13 | // QUICK CHAT DEMO 14 | form#demo(onsubmit='return false') 15 | h3 Quick Chat Demo 16 | h5 Open this page in multiple tabs or browsers and type a message below 17 | #chatlog 18 | input(id='myMessage', type='text', autocomplete='off') 19 | -------------------------------------------------------------------------------- /test/fixtures/project/client/views/main.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | html(lang="en") 3 | head 4 | != SocketStream 5 | meta(charset="utf-8") 6 | title Welcome 7 | body 8 | #content 9 | a(href='https://github.com/socketstream/socketstream') 10 | img(src='/images/logo.png', alt='SocketStream Logo', width=167, height=159) 11 | h1 Welcome to your new realtime app! 12 | 13 | // QUICK CHAT DEMO 14 | form#demo(onsubmit='return false') 15 | h3 Quick Chat Demo 16 | h5 Open this page in multiple tabs or browsers and type a message below 17 | #chatlog 18 | input(id='myMessage', type='text', autocomplete='off') 19 | -------------------------------------------------------------------------------- /test/helpers/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | 4 | module.exports = { 5 | 6 | /** 7 | * Return function name 8 | * 9 | * Example: 10 | * var f = function myFunctionName() { 11 | * return false; 12 | * } 13 | * 14 | * getfunctionName(f); // returns 'myFunctionName' 15 | * @param {Function} fun Function with the name to return 16 | * @return {String} Passed function name 17 | */ 18 | getfunctionName: function(fun) { 19 | var ret = fun.toString(); 20 | ret = ret.substr('function '.length); 21 | ret = ret.substr(0, ret.indexOf('(')); 22 | return ret; 23 | } 24 | } -------------------------------------------------------------------------------- /docs/partials/api/ss.server.server.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

server 2 |
service in module ss 3 | 4 |
5 |

6 |

Description

7 |

Server parts used while running

8 |
9 |
10 | -------------------------------------------------------------------------------- /lib/cli/index.js: -------------------------------------------------------------------------------- 1 | // Parses commands from calling 'socketstream' on the CLI 2 | // 3 | 'use strict'; 4 | 5 | 6 | 7 | // Dependencies 8 | // 9 | var generator = require('./generate'); 10 | 11 | 12 | 13 | /** 14 | * Processes the command line arguments passed to SocketStream 15 | * 16 | * @param {[type]} program The commander program instance 17 | * @return {Void} 18 | */ 19 | exports.process = function (program) { 20 | switch (program.args[0]) { 21 | 22 | // Create a new project 23 | case 'new': 24 | case 'n': 25 | return generator.generate(program); 26 | default: 27 | return console.log('Type "socketstream new " to create a new application'); 28 | } 29 | }; -------------------------------------------------------------------------------- /docs/partials/api/ss.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

2 |
3 |
4 |

5 |

It reflects a similar API to the client API.

6 |

Internal API object which is passed to sub-modules and can be used within your app. 7 | Use with caution.

8 |

To access it without it being passed var ss = require('socketstream').api;

9 |
10 | -------------------------------------------------------------------------------- /test/unit/websocket/transports/engineio/wrapper.test.js: -------------------------------------------------------------------------------- 1 | describe("wrapper", function(){ 2 | 3 | describe("#connect", function(){ 4 | 5 | describe("open event", function(){ 6 | 7 | it("should retrieve the session id from the connect.sid cookie"); 8 | // We need a way to simulate the browser 9 | // We need to set a cookie on the browser 10 | // We need to load the javascript into that browser's page 11 | // We need to run the script which attempts to connect to the server 12 | 13 | it("should send a message back to the server with the session id, if a session id is present"); 14 | 15 | it("should raise an error if if is not able to obtain a connect session id"); 16 | 17 | }); 18 | 19 | }); 20 | 21 | }); 22 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | docs 3 | docs/js/docs-setup.js 4 | lib/client/bundler/browserify.client.js 5 | lib/client/system/modules/eventemitter2.js 6 | lib/request/responders/events/client.js 7 | lib/websocket/transports/sockjs/lib.min.js 8 | node_modules 9 | test/fixtures/project/client/code 10 | test/fixtures/project/client/static/assets 11 | test/fixtures/project/app.js 12 | test/fixtures/project/node_modules 13 | Gruntfile.js 14 | **/*.min.js 15 | **/*.min.css 16 | 17 | # will be moved to ss-generator 18 | new_project 19 | lib/cli 20 | 21 | # TODO fix these files 22 | lib/websocket/transports/engineio/client.js 23 | lib/websocket/transports/engineio/index.js 24 | lib/websocket/transports/engineio/wrapper.js 25 | lib/websocket/transports/sockjs/wrapper.js 26 | test/unit 27 | -------------------------------------------------------------------------------- /new_project/server/rpc/demo.coffee: -------------------------------------------------------------------------------- 1 | # Server-side Code 2 | 3 | # Define actions which can be called from the client using ss.rpc('demo.ACTIONNAME', param1, param2...) 4 | exports.actions = (req, res, ss) -> 5 | 6 | # Example of pre-loading sessions into req.session using internal middleware 7 | req.use('session') 8 | 9 | # Uncomment line below to use the middleware defined in server/middleware/example 10 | #req.use('example.authenticated') 11 | 12 | sendMessage: (message) -> 13 | if message && message.length > 0 # Check for blank messages 14 | ss.publish.all('newMessage', message) # Broadcast the message to everyone 15 | res(true) # Confirm it was sent to the originating client 16 | else 17 | res(false) 18 | -------------------------------------------------------------------------------- /docs/partials/tutorials/http_middleware.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

HTTP Middleware

6 |

SocketStream no longer provides a stack of Connect HTTP middleware which is used internally to serve single-page clients, asset files and static files (e.g. images) in client/static.

7 |

Session handling is done in add-ons like socketstream-cookie-session.

8 |

For additional middleware follow the examples for Connect or ExpressJS.

9 |
10 | -------------------------------------------------------------------------------- /.javascript.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "asi": false, 4 | "bitwise": true, 5 | "browser": true, 6 | "node": true, 7 | "curly": true, 8 | "forin": true, 9 | "immed": true, 10 | "latedef": false, 11 | "maxlen": 150, 12 | "newcap": false, 13 | "noarg": true, 14 | "noempty": true, 15 | "nonew": true, 16 | "camelcase": false, 17 | "predef": [ 18 | "$", 19 | "jQuery", 20 | "alert", 21 | 22 | "jasmine", 23 | "beforeEach", 24 | "afterEach", 25 | "after", 26 | "before", 27 | "describe", 28 | "expect", 29 | "it", 30 | "xit", 31 | "should", 32 | "sinon", 33 | 34 | "angular", 35 | "inject", 36 | "module", 37 | "exports" 38 | ], 39 | "trailing": true, 40 | "eqnull": true, 41 | "undef": true, 42 | "unused": true 43 | } 44 | -------------------------------------------------------------------------------- /lib/client/formatters/html.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | 5 | /** 6 | * Plain HTML Formatter 7 | */ 8 | module.exports = function(ss) { 9 | return { 10 | extensions: ['html'], 11 | assetType: 'html', 12 | contentType: 'text/html', 13 | compile: function(path, options, cb) { 14 | ss.log.trace('Compiling plain HTML',path,options); 15 | var input; 16 | input = fs.readFileSync(path, 'utf8'); 17 | //TODO if no file, return error object 18 | 19 | // If passing optional headers for main view 20 | if (options && options.headers) { 21 | input = input.replace('', options.headers); 22 | input = input.replace('', options.headers); 23 | } 24 | return cb(input); 25 | } 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /docs/partials/api/events.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

events 2 |
3 |
4 |

5 |

Description

6 |

Internal Event bus.

7 |

Note: only used by the ss-console module for now. This idea will be expended upon in SocketStream 0.4

8 |

'server:start' is emitted when the server starts. If in production the assets will be saved before the event.

9 |
10 |
11 | -------------------------------------------------------------------------------- /new_project/client/views/app.demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome 7 | 8 | 9 |
10 | 11 | SocketStream Logo 12 | 13 |

Welcome to your new realtime app!

14 | 15 | 16 |
17 |

Quick Chat Demo

18 |
Open this page in multiple tabs or browsers and type a message below
19 |
20 | 21 |
22 |
23 | 24 | -------------------------------------------------------------------------------- /test/unit/test/test-socketstream.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fixtures = require( '../../fixtures'), 4 | ss = fixtures.socketstreamProject(), 5 | chai = require('chai'), 6 | expect = chai.expect; 7 | 8 | //ss.start('test-socketstream'); 9 | 10 | xdescribe('test-socketstream', function() { 11 | beforeEach(function(done) { 12 | ss.start('test-socketstream',done); 13 | }); 14 | 15 | describe('sendMessage', function() { 16 | 17 | it('should publish messages received', function(done) { 18 | var text = 'Hello World!'; 19 | ss.api.rpc('demo.sendMessage', text, function(res) { 20 | expect(res).to.eql([true]); 21 | //TODO published expectations 22 | // expect(..).to.equal({ status:'success', content:text}); 23 | done(); 24 | }) 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /docs/partials/api/ss.version.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

version 2 |
service in module ss 3 | 4 |
5 |

6 |

Usage

7 |
ss.version
8 |

Returns

number

major.minor

9 |
10 |
11 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/hot_to_test.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name How to test 3 | 4 | @description 5 | # How do I test? 6 | 7 | You can make automated test for your server code with Mocha or a similar framework. 8 | 9 | A simple test found in the demo app looks like, 10 | 11 | var ss, 12 | chai = require("chai"), 13 | expect = chai.expect; 14 | 15 | describe('Demo', function() { 16 | beforeEach(function(done) { 17 | ss = require("socketstream").start('test-socketstream',done); 18 | }); 19 | 20 | describe('sendMessage', function() { 21 | it('should publish messages received', function(done) { 22 | var text = 'Hello World!'; 23 | ss.rpc('demo.sendMessage', text, function(res) { 24 | expect(res).to.equal([true]); 25 | }) 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/how_to_write_css.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name How to write view CSS 3 | 4 | @description 5 | # How to write view CSS ? 6 | 7 | Views can be styles using plain CSS, Less, SASS or any other supported by CSS formatters. 8 | 9 | The CSS formatter is added automatically. Formatters for Less and SASS are builtin, but must 10 | be added and the core dependency added to your `package.json`. 11 | 12 | npm install sass --save-dev 13 | 14 | The SASS formatter is now builtin but optional. Add the following line to your app. 15 | 16 | ss.client.formatters.add('sass'); 17 | 18 | By default the `includePaths` option is the `node_modules` and the client directory. 19 | If you want to change it you can override it. 20 | 21 | ss.client.formatters.add('sass', { 22 | includePaths: [ path.join(__dirname,'client','sass')] 23 | }); 24 | -------------------------------------------------------------------------------- /lib/request/middleware/index.js: -------------------------------------------------------------------------------- 1 | // Request Middleware 2 | // ------------------ 3 | // Allows incoming requests to be pre-processed, transformed, or sent elsewhere 4 | 'use strict'; 5 | 6 | var pathlib = require('path'), 7 | fs = require('fs'), 8 | apiTree = require('apitree'); 9 | 10 | module.exports = function(ss) { 11 | // Load internal middleware 12 | var internal = require('./internal')(ss); 13 | return { 14 | 15 | // Return API 16 | load: function() { 17 | 18 | var customDir = pathlib.join(ss.root, 'server/middleware'); 19 | 20 | // Load custom middleware 21 | var stack = fs.existsSync(customDir) && apiTree.createApiTree(customDir) || {}; 22 | 23 | // Append internal/default middleware 24 | for (var k in internal) { 25 | stack[k] = internal[k]; 26 | } 27 | return stack; 28 | } 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /docs/partials/api/client.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

client 2 |
3 |
4 |

5 |

Description

6 |

Client serving, bundling, development, building.

7 |

One or more clients are defined and will be served in production as a single HTML, CSS, and JS file.

8 |

This is for the module returned by require('socketstream').client.

9 |
10 |
11 | -------------------------------------------------------------------------------- /express.js: -------------------------------------------------------------------------------- 1 | // Entry point for app.js 2 | 'use strict'; 3 | 4 | var ss = require('./lib/socketstream.js'), 5 | express = ss.api.require('express'); 6 | 7 | module.exports = function() { 8 | var app = ss.http.middleware = express(); 9 | 10 | app.listen = function() { 11 | ss.ws.listen.apply(ss.ws, arguments); 12 | }; 13 | /* TODO 14 | app.socketstream = function( fn(req,stream) ) 15 | 16 | app.stream = ss.http.stream; 17 | */ 18 | app.stream = ss.http.stream; 19 | 20 | if (ss.env === 'development') { 21 | app.set('views', ss.client.dirs.views); 22 | // Showing stack errors 23 | app.set('showStackError', true); 24 | // Disable views cache 25 | app.set('view cache', false); 26 | 27 | // Environment dependent middleware 28 | // throws cannot find stack 29 | // ss.api.require('express-debug')(app, {/* settings */}); 30 | } 31 | 32 | return app; 33 | }; 34 | -------------------------------------------------------------------------------- /misc/changelog.tpl.md: -------------------------------------------------------------------------------- 1 | # <%= version%> (<%= today%>) 2 | <% if (_(changelog.feat).size() > 0) { %> 3 | ## Features 4 | <% _(changelog.feat).keys().sort().forEach(function(scope) { %> 5 | - **<%= scope%>:** <% changelog.feat[scope].forEach(function(change) { %> 6 | - <%= change.msg%> (<%= helpers.commitLink(change.sha1) %>) <% }); %><% }); %> <% } %> 7 | <% if (_(changelog.fix).size() > 0) { %> 8 | ## Bug Fixes 9 | <% _(changelog.fix).keys().sort().forEach(function(scope) { %> 10 | - **<%= scope%>:** <% changelog.fix[scope].forEach(function(change) { %> 11 | - <%= change.msg%> (<%= helpers.commitLink(change.sha1) %>) <% }); %><% }); %> <% } %> 12 | <% if (_(changelog.breaking).size() > 0) { %> 13 | ## Breaking Changes 14 | <% _(changelog.breaking).keys().sort().forEach(function(scope) { %> 15 | - **<%= scope%>:** <% changelog.breaking[scope].forEach(function(change) { %> 16 | <%= change.msg%><% }); %><% }); %> <% } %> -------------------------------------------------------------------------------- /new_project/server/rpc/demo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Server-side Code 3 | 4 | // Define actions which can be called from the client using ss.rpc('demo.ACTIONNAME', param1, param2...) 5 | exports.actions = function(req, res, ss) { 6 | 7 | // Example of pre-loading sessions into req.session using internal middleware 8 | req.use('session'); 9 | 10 | // Uncomment line below to use the middleware defined in server/middleware/example 11 | //req.use('example.authenticated') 12 | 13 | return { 14 | 15 | sendMessage: function(message) { 16 | if (message && message.length > 0) { // Check for blank messages 17 | ss.publish.all('newMessage', message); // Broadcast the message to everyone 18 | return res(true); // Confirm it was sent to the originating client 19 | } else { 20 | return res(false); 21 | } 22 | } 23 | 24 | }; 25 | 26 | }; -------------------------------------------------------------------------------- /src/docs/tutorials/en/modules.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Modules 3 | 4 | @description 5 | # Loading Modules from app 6 | 7 | ## Development Dependencies 8 | 9 | During development the bundlers, formatters and template engines are loaded as plugins using `ss.require`. This allows you to not require development time code in your `app.js`. If you choose to build production assets beforehand, you can keep these dependencies as development dependencies and only install production dependencies. 10 | 11 | ### Internal require 12 | 13 | The internal require is used to load modules that may be built in or supplied by the project. It is a function on the API passed to plugins and used internally. 14 | 15 | `ss.require(id,builtinPath,defaultId)` The builtin path is the relative path within socketstream 16 | where the builtin modules are found. The defaultId is the extension-less filename loaded when the main id isn't matched. 17 | -------------------------------------------------------------------------------- /test/fixtures/project/server/rpc/demo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Server-side Code 3 | 4 | // Define actions which can be called from the client using ss.rpc('demo.ACTIONNAME', param1, param2...) 5 | exports.actions = function(req, res, ss) { 6 | 7 | // Example of pre-loading sessions into req.session using internal middleware 8 | req.use('session'); 9 | 10 | // Uncomment line below to use the middleware defined in server/middleware/example 11 | //req.use('example.authenticated') 12 | 13 | return { 14 | 15 | sendMessage: function(message) { 16 | if (message && message.length > 0) { // Check for blank messages 17 | ss.publish.all('newMessage', message); // Broadcast the message to everyone 18 | return res(true); // Confirm it was sent to the originating client 19 | } else { 20 | return res(false); 21 | } 22 | } 23 | 24 | }; 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /test/unit/client/http.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | 5 | describe('HTTP middleware', function () { 6 | 7 | 8 | describe('#set', function () { 9 | 10 | 11 | 12 | it('should allow the user to set options for the http middleware'); 13 | 14 | 15 | 16 | }); 17 | 18 | 19 | 20 | describe('#load', function () { 21 | 22 | 23 | 24 | it('should load SocketStream\'s middleware first'); 25 | 26 | 27 | 28 | it('should then load any app-specific middleware next'); 29 | 30 | 31 | 32 | it('should finally load the static asset serving middleware last'); 33 | 34 | 35 | 36 | }); 37 | 38 | 39 | 40 | describe('#route', function () { 41 | 42 | 43 | 44 | it('should if given a url and function, route requests for that url to that function'); 45 | 46 | 47 | 48 | it('should if given just a url, return a serveClient function'); 49 | 50 | 51 | 52 | }); 53 | 54 | 55 | 56 | }); -------------------------------------------------------------------------------- /docs/partials/api/ss.env.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

env 2 |
service in module ss 3 | 4 |
5 |

6 |

Usage

7 |
ss.env
8 |

Returns

string

Execution environment type. To change set environment variable NODE_ENV or SS_ENV. 'development' by default.

9 |
10 |
11 | -------------------------------------------------------------------------------- /docs/partials/api/ss.root.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

root 2 |
service in module ss 3 | 4 |
5 |

6 |

Description

7 |

By default the project root is the current working directory

8 |
9 |

Usage

10 |
ss.root
11 |

Returns

string

Project root

12 |
13 |
14 | -------------------------------------------------------------------------------- /test/fixtures/project/app.js: -------------------------------------------------------------------------------- 1 | // My SocketStream app 2 | 3 | var http = require('http') 4 | , ss = require('socketstream'); 5 | 6 | ss.client.define('main', { 7 | view: 'app.jade', 8 | css: ['libs', 'app.styl'], 9 | code: ['libs', 'modules', 'main'] 10 | }); 11 | 12 | ss.http.router.on('/', function(req, res) { 13 | res.serve('main'); 14 | }); 15 | 16 | // Remove to use only plain .js, .html and .css files if you prefer 17 | ss.client.formatters.add(require('ss-coffee')); //TODO switch to internal 18 | ss.client.formatters.add(require('ss-jade')); 19 | ss.client.formatters.add(require('ss-stylus')); 20 | 21 | // Use server-side compiled Hogan (Mustache) templates. Others engines available 22 | ss.client.templateEngine.use(require('ss-hogan')); 23 | 24 | ss.ws.transport.use('sockjs'); 25 | 26 | // Minimise and pack assets if you type SS_ENV=production node app.js 27 | if (ss.env == 'production') ss.client.packAssets(); 28 | 29 | var server = http.Server(ss.http.middleware); 30 | server.listen(3000); 31 | 32 | ss.start(server); 33 | -------------------------------------------------------------------------------- /test/fixtures/project/client/css/demo.styl: -------------------------------------------------------------------------------- 1 | // Quick Chat Demo 2 | 3 | @import 'helpers' 4 | 5 | #demo 6 | border 1px solid #ccc 7 | width 700px 8 | text-align left 9 | margin 50px auto 10 | padding 15px 11 | round 5px 12 | h3 13 | margin 0 14 | font-size 1.0em 15 | font-weight normal 16 | h5 17 | margin 0 18 | padding 5px 0 19 | color #666 20 | font-weight normal 21 | font-size 0.8em 22 | #myMessage, #chatlog 23 | border none 24 | box-shadow inset 0 0 2px #777 25 | round 5px 26 | #myMessage 27 | width 690px 28 | padding 5px 29 | margin 0 30 | #chatlog 31 | width 700px 32 | background-color white 33 | margin 5px 0 34 | p 35 | font-size 0.9em 36 | color #000a68 37 | padding 3px 9px 38 | margin 0 39 | p:first-child 40 | padding-top 9px 41 | p:last-child 42 | padding-bottom 9px 43 | span.time 44 | color #666 45 | float right 46 | width 70px 47 | text-align right 48 | font-size 0.7em 49 | -------------------------------------------------------------------------------- /test/unit/client/abcClient.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ss = require( '../../../lib/socketstream'); 4 | 5 | module.exports = defineAbcClientAndLoad; 6 | 7 | function defineAbcClient(conf) { 8 | var r = { 9 | css: './abc/style.css', 10 | code: './abc/index.js', 11 | view: './abc/abc.html', 12 | tmpl: './templates/abc/1.html' 13 | }; 14 | for(var k in conf) { 15 | if (conf[k] === undefined) { delete r[k]; } 16 | else if (k !== 'custom') { r[k] = conf[k]; } 17 | } 18 | return conf.custom? ss.client.define('abc',conf.custom,r) : ss.client.define('abc',r); 19 | } 20 | 21 | function defineAbcClientAndLoad(conf,run,load) { 22 | ss.client.init(); 23 | 24 | var client = defineAbcClient(conf); 25 | if (run) { 26 | run(); 27 | } 28 | if (load !== false) { 29 | ss.client.load(); 30 | 31 | ss.tasks.defaults(); 32 | } 33 | 34 | return client; 35 | } 36 | 37 | module.exports.reset = function() { 38 | ss.client.unload(); 39 | ss.client.forget(); 40 | ss.client.init(); 41 | ss.tasks.unload(); 42 | ss.tasks.forget(); 43 | }; 44 | -------------------------------------------------------------------------------- /docs/partials/api/start.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

start 2 |
3 |
4 |

5 |

Description

6 |

Starts the development or production server

7 |
8 |

Usage

9 |
new start(server);
10 |

Parameters

ParamTypeDetails
serverHTTPServer

Instance of the server from the http module

11 |
12 |
13 | -------------------------------------------------------------------------------- /test/fixtures/project/node_modules/object-assign/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 'use strict'; 3 | var hasOwnProperty = Object.prototype.hasOwnProperty; 4 | var propIsEnumerable = Object.prototype.propertyIsEnumerable; 5 | 6 | function toObject(val) { 7 | if (val === null || val === undefined) { 8 | throw new TypeError('Object.assign cannot be called with null or undefined'); 9 | } 10 | 11 | return Object(val); 12 | } 13 | 14 | module.exports = Object.assign || function (target, source) { 15 | var from; 16 | var to = toObject(target); 17 | var symbols; 18 | 19 | for (var s = 1; s < arguments.length; s++) { 20 | from = Object(arguments[s]); 21 | 22 | for (var key in from) { 23 | if (hasOwnProperty.call(from, key)) { 24 | to[key] = from[key]; 25 | } 26 | } 27 | 28 | if (Object.getOwnPropertySymbols) { 29 | symbols = Object.getOwnPropertySymbols(from); 30 | for (var i = 0; i < symbols.length; i++) { 31 | if (propIsEnumerable.call(from, symbols[i])) { 32 | to[symbols[i]] = from[symbols[i]]; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return to; 39 | }; 40 | -------------------------------------------------------------------------------- /test/unit/utils/log.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ss = require( '../../../lib/socketstream'), 4 | log = require( '../../../lib/utils/log'); 5 | 6 | describe('lib/utils/log', function() { 7 | ss.api.publish = { 8 | all: function() {} 9 | }; 10 | 11 | it('should no longer be a function', function(done) { 12 | log.should.have.a.type('object'); 13 | done(); 14 | }); 15 | it('should have a function property #debug', function(done) { 16 | log.should.have.a.property('debug').with.a.type('function'); 17 | done(); 18 | }); 19 | it('should have a function property #info', function(done) { 20 | log.should.have.a.property('info').with.a.type('function'); 21 | done(); 22 | }); 23 | it('should have a function property #warn', function(done) { 24 | log.should.have.a.property('warn').with.a.type('function'); 25 | done(); 26 | }); 27 | it('should have a function property #error', function(done) { 28 | log.should.have.a.property('error').with.a.type('function'); 29 | done(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2013 Owen Barnes 2 | 3 | The MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/client_side_development.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Client-Side Development 3 | 4 | @description 5 | # Client-Side Development 6 | 7 | Each view is served with a separate bundle of assets. A HTML, JS and CSS file makes up the view. 8 | 9 | The production form will, 10 | 11 | * be saved in the `assets` (configurable) directory and served under the `/assets` path. 12 | * Have the same path and name 13 | * have an extending that determines the type 14 | * Follow the form `assets//.`. 15 | 16 | Each of the entries that make of the bundles are served separately to the browser while developing. 17 | 18 | * The base path is the same as in production 19 | * Additional identification will be passed with a query string 20 | * The client and bundler is determined by the `name` part of the path. 21 | * A parameter determines the entry (_,loader,mod,start) 22 | 23 | The URL pattern is `http:///assets//.?_=`. 24 | 25 | Bundlers generally work within the client directory to reduce the amount of files to watch. The path 26 | of entries is normally resolved relative to the client directory. 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /docs/partials/tutorials/hot_to_test.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

How do I test?

6 |

You can make automated test for your server code with Mocha or a similar framework.

7 |

A simple test found in the demo app looks like,

8 |
var ss,
 9 |     chai = require("chai"),
10 |     expect = chai.expect;
11 | 
12 | describe('Demo', function() {
13 |   beforeEach(function(done) {
14 |     ss = require("socketstream").start('test-socketstream',done);
15 |   });
16 | 
17 |   describe('sendMessage', function() {
18 |     it('should publish messages received', function(done) {
19 |       var text = 'Hello World!';
20 |       ss.rpc('demo.sendMessage', text, function(res) {
21 |         expect(res).to.equal([true]);
22 |       })
23 |     });
24 |   });
25 | });
26 | 
27 | -------------------------------------------------------------------------------- /test/fixtures/project/node_modules/mock-socket/index.js: -------------------------------------------------------------------------------- 1 | // Engine.IO server-side wrapper 2 | 'use strict'; 3 | 4 | var fs = require('fs'); 5 | 6 | var openSocketsById = {}; 7 | 8 | module.exports = function(ss, messageEmitter, httpServer, config){ 9 | config = config || {}; 10 | config.server = config.server || {}; 11 | config.client = config.client || {}; 12 | 13 | var api = { event: eventFn }; 14 | 15 | return api; 16 | 17 | function eventFn() { 18 | return { 19 | 20 | // Send the same message to every open socket 21 | all: function(msg) { 22 | for (var id in openSocketsById) { 23 | if (openSocketsById.hasOwnProperty(id)) { 24 | openSocketsById[id].send('0|' + msg + '|null'); 25 | } 26 | } 27 | }, 28 | 29 | // Send an event to a specific socket 30 | // Note: 'meta' is typically the name of the channel 31 | socketId: function(id, msg, meta) { 32 | var socket = openSocketsById[id]; 33 | if (socket) { 34 | return socket.send('0|' + msg + '|' + meta); 35 | } else { 36 | return false; 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/request/responders/events/index.js: -------------------------------------------------------------------------------- 1 | // Browser Events Responder 2 | // ------------------------ 3 | // Takes incoming event message types and converts them into a format suitable for sending over the websocket 4 | 'use strict'; 5 | 6 | 7 | var fs = require('fs'), 8 | path = require('path'), 9 | debug = require('debug')('socketstream:responder'); 10 | 11 | module.exports = function(responderId, config, ss) { 12 | var code, name; 13 | name = config && config.name || 'events'; 14 | 15 | // Serve client code 16 | code = fs.readFileSync(path.join(__dirname, 'client.js'), 'utf8'); 17 | ss.client.send('mod', 'events-responder', code, {}); //TODO define file instead. client manager will load/reload as needed 18 | ss.client.send('code', 'init', 'require(\'events-responder\')(' + responderId + ', {}, require(\'socketstream\').send(' + responderId + '));'); 19 | debug('events-responder client ready.'); 20 | 21 | // Return API 22 | return { 23 | name: name, 24 | interfaces: function() { 25 | return { 26 | websocket: function(msg, meta, send) { 27 | return send(JSON.stringify(msg)); 28 | } 29 | }; 30 | } 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /lib/publish/transports/redis.js: -------------------------------------------------------------------------------- 1 | // Publish Event - Redis Transport 2 | 'use strict'; 3 | 4 | var redis = require('redis'); //TODO ss.require('redis') 5 | 6 | module.exports = function(config) { 7 | var conn, host, options, port; 8 | if (!config) { 9 | config = {}; 10 | } 11 | 12 | // Set options or use the defaults 13 | port = config.port || 6379; 14 | host = config.host || '127.0.0.1'; 15 | options = config.options || {}; 16 | 17 | // Redis requires a separate connection for pub/sub 18 | conn = {}; 19 | ['pub', 'sub'].forEach(function(name) { 20 | conn[name] = redis.createClient(port, host, options); 21 | if (config.pass) { 22 | conn[name].auth(config.pass); 23 | } 24 | if (config.db) { 25 | return conn[name].select(config.db); 26 | } 27 | }); 28 | return { 29 | listen: function(cb) { 30 | conn.sub.subscribe('ss:event'); 31 | return conn.sub.on('message', function(channel, msg) { 32 | return cb(JSON.parse(msg)); 33 | }); 34 | }, 35 | send: function(obj) { 36 | var msg; 37 | msg = JSON.stringify(obj); 38 | return conn.pub.publish('ss:event', msg); 39 | } 40 | }; 41 | }; 42 | -------------------------------------------------------------------------------- /lib/request/responders/events/client.js: -------------------------------------------------------------------------------- 1 | // Tell the browser how to respond to incoming events 2 | 'use strict'; 3 | 4 | var EventEmitter2, ss; 5 | 6 | EventEmitter2 = require('eventemitter2').EventEmitter2; 7 | 8 | ss = require('socketstream'); 9 | 10 | module.exports = function(responderId, config, send) { 11 | var EE2; 12 | EE2 = new EventEmitter2; 13 | 14 | // Expose the Event Emitter on 'ss.event' so it can be used in applications 15 | ss.registerApi('event', EE2); 16 | 17 | // RECEIVING 18 | return ss.message.on(responderId, function(msg, meta) { 19 | var args, ee, obj; 20 | obj = JSON.parse(msg); // events are sent as JSON messages 21 | args = [obj.e]; // first param is the event name 22 | args = args.concat(obj.p); // add n params 23 | (meta != null) && args.push(meta); // last param is optional meta data (e.g. what channel was this sent to) 24 | 25 | // Select event emitter based on whether this is an internal system event or application event 26 | ee = obj.e && obj.e.substr(0, 5) === '__ss:' && ss.server || EE2; 27 | 28 | // Emit event 29 | return ee.emit.apply(ee, args); 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /test/unit/client/template_engines/ember.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'), 4 | ss = require( '../../../../lib/socketstream'), 5 | options = ss.client.options, 6 | fixtures = require('../../../fixtures'); 7 | 8 | describe('Ember.js template engine', function () { 9 | 10 | fixtures.setProject('project'); 11 | 12 | options.liveReload = false; 13 | 14 | beforeEach(function() { 15 | ss.client.reset(); 16 | ss.client.set({liveReload:false}); 17 | }); 18 | 19 | it('should output an inline template for use with Ember.js', function(done) { 20 | 21 | ss.client.define('abc', { 22 | css: './abc/style.css', 23 | code: './abc/index.a', 24 | view: './abc/abc.html' 25 | }); 26 | 27 | ss.client.templateEngine.use('ember'); 28 | 29 | ss.client.load(); 30 | 31 | var bundler = ss.api.bundler.get('abc'); 32 | 33 | var files = [ bundler.entryFor('tmpl','client/templates/1.html') ]; 34 | 35 | ss.client.templateEngine.generate(bundler, files, function(tag) { 36 | tag.should.be.equal(''); 37 | done(); 38 | }); 39 | 40 | }); 41 | 42 | }); 43 | -------------------------------------------------------------------------------- /test/fixtures/project/node_modules/object-assign/license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Sindre Sorhus (sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/partials/tutorials/how_to_write_css.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

How to write view CSS ?

6 |

Views can be styles using plain CSS, Less, SASS or any other supported by CSS formatters.

7 |

The CSS formatter is added automatically. Formatters for Less and SASS are builtin, but must 8 | be added and the core dependency added to your package.json.

9 |
npm install sass --save-dev
10 | 

The SASS formatter is now builtin but optional. Add the following line to your app.

11 |
ss.client.formatters.add('sass');
12 | 

By default the includePaths option is the node_modules and the client directory. 13 | If you want to change it you can override it.

14 |
ss.client.formatters.add('sass', {
15 |   includePaths: [ path.join(__dirname,'client','sass')]
16 | });
17 | 
18 | -------------------------------------------------------------------------------- /lib/client/formatters/jade.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'), 4 | path = require('path'); 5 | 6 | module.exports = function(ss, config, clientOptions) { 7 | config = config || {}; 8 | if (!config.basedir) { 9 | config.basedir = path.join(ss.root, clientOptions.dirs.client); 10 | } 11 | 12 | var jade = ss.require('jade'); 13 | 14 | return { 15 | 16 | name: 'Jade', 17 | extensions: ['jade'], 18 | assetType: 'html', 19 | contentType: 'text/html', 20 | 21 | compile: function(path, options, cb) { 22 | 23 | var locals = {}; 24 | 25 | // Merge any locals passed to config.locals 26 | if (options.locals && typeof(options.locals) === 'object') { 27 | for (var attrname in options.locals) { locals[attrname] = options.locals[attrname]; } 28 | } 29 | 30 | // If passing optional headers for main view HTML 31 | if (options && options.headers) { 32 | locals.SocketStream = options.headers; 33 | } 34 | 35 | var input = fs.readFileSync(path, 'utf8'); 36 | var parser = jade.compile(input, Object.create(config, { 37 | filename: {value:path} 38 | }) ); 39 | var output = parser(locals); 40 | 41 | cb(output); 42 | } 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /lib/publish/transport.js: -------------------------------------------------------------------------------- 1 | // Publish Event Transport 2 | // ----------------------- 3 | // Right now you can either use the internal transport or inbuilt Redis module 4 | // The idea behind making this modular is to allow others to experiment with other message queues / servers 5 | 6 | 'use strict'; 7 | 8 | module.exports = function() { 9 | var config, transport; 10 | transport = null; 11 | config = {}; 12 | return { 13 | use: function(nameOrModule, cfg) { 14 | var modPath; 15 | if (!cfg) { 16 | cfg = {}; 17 | } 18 | config = cfg; 19 | transport = (function() { 20 | if (typeof nameOrModule === 'function') { 21 | return nameOrModule; 22 | } else { 23 | modPath = './transports/' + nameOrModule; 24 | if (require.resolve(modPath)) { 25 | return require(modPath); 26 | } else { 27 | throw new Error('Unable to find Publish Event Transport \'' + nameOrModule + '\' internally. Please pass a module'); 28 | } 29 | } 30 | })(); 31 | }, 32 | load: function() { 33 | if (!transport) { 34 | this.use('internal'); 35 | } 36 | return transport(config); //TODO should this be (ss,config) ? 37 | } 38 | }; 39 | }; 40 | -------------------------------------------------------------------------------- /docs/css/animations.css: -------------------------------------------------------------------------------- 1 | .slide-reveal.ng-enter { 2 | -webkit-transition:0.5s linear all; 3 | -moz-transition:0.5s linear all; 4 | -o-transition:0.5s linear all; 5 | transition:0.5s linear all; 6 | 7 | opacity:0.5; 8 | position:relative; 9 | opacity:0; 10 | top:10px; 11 | } 12 | .slide-reveal.ng-enter-active { 13 | top:0; 14 | opacity:1; 15 | } 16 | 17 | .expand.ng-enter { 18 | -webkit-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 19 | -moz-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 20 | -o-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 21 | transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 22 | 23 | opacity:0; 24 | max-height:0; 25 | overflow:hidden; 26 | } 27 | .expand.ng-enter-active { 28 | opacity:1; 29 | max-height:40px; 30 | } 31 | 32 | .expand.ng-leave { 33 | -webkit-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 34 | -moz-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 35 | -o-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 36 | transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all; 37 | 38 | opacity:1; 39 | max-height:40px; 40 | overflow:hidden; 41 | } 42 | .expand.ng-leave-active { 43 | opacity:0; 44 | max-height:0; 45 | } 46 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise" : true, 3 | "curly" : true, 4 | "eqeqeq" : true, 5 | "eqnull" : true, 6 | "immed" : true, 7 | "latedef" : false, 8 | "newcap" : true, 9 | "noarg" : true, 10 | "noempty" : true, 11 | "nonew" : true, 12 | "regexp" : true, 13 | "undef" : true, 14 | "strict" : true, 15 | "trailing" : true, 16 | "asi" : true, 17 | "laxcomma" : true, 18 | "loopfunc" : false, 19 | "sub" : true, 20 | "supernew" : true, 21 | "node" : true, 22 | "unused" : true, 23 | "multistr" : true, 24 | "maxlen" : 150, 25 | "smarttabs": true, 26 | "globals" : 27 | { 28 | "it" : false, 29 | "xit" : false, 30 | "describe" : false, 31 | "xdescribe" : false, 32 | "before" : false, 33 | "after" : false, 34 | "afterEach" : false, 35 | "beforeEach" : false, 36 | "spyOn" : false, 37 | "should": false, 38 | "sinon": false, 39 | "SockJS": false, 40 | "jQuery" : false, 41 | "$" : false, 42 | "alert" : false, 43 | "Worker" : false, 44 | "window" : false, 45 | "document" : false 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/websocket/transport.js: -------------------------------------------------------------------------------- 1 | // Websocket Transport 2 | // ------------------- 3 | // Define the transport to carry all realtime requests 4 | // Uses 'engineio' by default. See README to see how to configure it 5 | 'use strict'; 6 | 7 | module.exports = function(ss, emitter) { 8 | var mid, 9 | active, 10 | config = {}; 11 | 12 | return { 13 | get http() { 14 | return active? active.http: null; 15 | }, 16 | get ws() { 17 | return active? active.ws: null; 18 | }, 19 | use: function(nameOrModule, cfg) { 20 | mid = nameOrModule; 21 | config = cfg || config; 22 | 23 | // log warning 24 | // if (ss.require.resolve(nameOrModule, 'websocket/transports') == null) { 25 | // throw new Error('Unable to find the \'' + nameOrModule + '\' websocket transport internally'); 26 | // } 27 | }, 28 | load: function() { 29 | //TODO error handle missing require from within the sockjs/engineio module 30 | var transport = ss.require(mid, 'websocket/transports', 'engineio'); 31 | if (typeof transport !== 'function') { 32 | ss.log.error('Transport for "'+mid+'" must be a function(ss, emitter, config) default:', transport); 33 | } else { 34 | return (active = transport(ss, emitter, config)); 35 | } 36 | } 37 | }; 38 | }; 39 | -------------------------------------------------------------------------------- /docs/partials/api/set.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

set 2 |
3 |
4 |

5 |

Description

6 |

Overrides settings for root/client/server.

7 |
8 |

Usage

9 |
new set(where, what);
10 |

Parameters

ParamTypeDetails
whereString

Path in settings or '*'

11 |
whatStringObject

Value for setting or object when using star.

12 |
13 |
14 | -------------------------------------------------------------------------------- /new_project/client/css/libs/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /lib/client/formatters/sass.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'), 4 | findup = require('findup-sync'); 5 | 6 | module.exports = function(ss, config, clientOptions) { 7 | 8 | var sass = ss.require('node-sass'), 9 | modulesPath = [ findup('node_modules') ]; //TODO get real npm and bower package locations 10 | 11 | console.log( ); 12 | 13 | return { 14 | 15 | name: 'Sass', 16 | 17 | extensions: ['sass', 'scss'], 18 | 19 | assetType: 'css', 20 | 21 | contentType: 'text/css', 22 | 23 | compile: function(srcPath, options, success, error) { 24 | 25 | var opts = { 26 | file: srcPath, 27 | includePaths: modulesPath.concat([ path.join(ss.root, clientOptions.dirs.client) ]) 28 | }; 29 | for(var n in config) { 30 | if (n !== 'file') { 31 | opts[n] = config[n] || opts[n]; 32 | } 33 | } 34 | opts.locals = opts.locals || options.locals; 35 | opts.outputStyle = options.compress? "compressed":"nested"; 36 | 37 | sass.render(opts, function(err, result) { 38 | if (err) { 39 | console.log(err) 40 | return error(err); 41 | } 42 | success(result.css.toString()); 43 | //TODO result.stats print 44 | //TODO result.map.toString 45 | }); 46 | } 47 | }; 48 | }; 49 | -------------------------------------------------------------------------------- /test/fixtures/project/client/css/libs/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /docs/partials/tutorials/modules.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

Loading Modules from app

6 |

Development Dependencies

7 |

During development the bundlers, formatters and template engines are loaded as plugins using ss.require. This allows you to not require development time code in your app.js. If you choose to build production assets beforehand, you can keep these dependencies as development dependencies and only install production dependencies.

8 |

Internal require

9 |

The internal require is used to load modules that may be built in or supplied by the project. It is a function on the API passed to plugins and used internally.

10 |

ss.require(id,builtinPath,defaultId) The builtin path is the relative path within socketstream 11 | where the builtin modules are found. The defaultId is the extension-less filename loaded when the main id isn't matched.

12 |
13 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/url_scheme.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name URL Scheme 3 | 4 | @description 5 | # URL Scheme 6 | 7 | The common URL for a view is its name at the root level, but you can choose whatever you will calling `serveClient(..)`. 8 | 9 | ## Assets 10 | 11 | The contents of the client assets directory will be served under `/assets`. 12 | 13 | 14 | When views are packed for production they are saved under the client assets directory. This will change in the future 15 | to make relative URLs work the same in development and production. 16 | 17 | ## Middleware 18 | 19 | At development time middleware is added to serve HTML, JS and CSS on the fly. 20 | 21 | 22 | ## Serving CSS 23 | 24 | CSS files are served under /assets//123.css in production. When served ad hoc in development, and on-demand in 25 | production all CSS must be served on the same level and ideally in an equivalent URL. 26 | 27 | ## JS Module Paths 28 | 29 | 30 | ## On Demand Loading 31 | 32 | The current on-demand fetching of JS is handled by middleware. It should be possible to do it using static files. 33 | 34 | In production it would make sense to support a path like `/assets/require/..`. 35 | 36 | We will have to consider whether all client code is considered completely open, or only partially. Should all client 37 | modules be exported in minified form, or only those in a whitelist. -------------------------------------------------------------------------------- /src/docs/tutorials/en/start_targets.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Start Targets 3 | 4 | @description 5 | # Start Targets 6 | 7 | When SocketStream is started in runs on an Orchestrator just like the build tool Gulp. 8 | 9 | Note that this interface is experimental and may change 10 | 11 | #### Starting the server 12 | 13 | The `default` target will build assets and start a web server as needed. If other named targets 14 | are specified they will be started instead. 15 | 16 | To start your socketstream project with different build targets specify them after the server 17 | object, or instead if you do not want to run the streaming server. 18 | 19 | // Start web server 20 | var server = http.Server(ss.http.middleware); 21 | server.listen(3000); 22 | 23 | // Start SocketStream 24 | ss.start(server,'my-task','serve'); 25 | 26 | #### Default tasks 27 | 28 | The default task depends on 29 | 30 | * `pack-all`/`pack-if-needed` if using packed assets 31 | * `live-assets` if not using packed assets 32 | * `live-reload` if live reload is enabled (true by default) 33 | * and `serve-on-demand` 34 | 35 | #### Hooking in 36 | 37 | If you want to react when things happen around tasks you can register event handlers. 38 | The tasks are managed by Orchestrator which manages tasks in Gulp. 39 | 40 | Register events on [ss.api.orchestrator](https://www.npmjs.com/package/orchestrator#orchestrator-on-event-cb) 41 | -------------------------------------------------------------------------------- /test/fixtures/project/node_modules/object-assign/readme.md: -------------------------------------------------------------------------------- 1 | # object-assign [![Build Status](https://travis-ci.org/sindresorhus/object-assign.svg?branch=master)](https://travis-ci.org/sindresorhus/object-assign) 2 | 3 | > ES6 [`Object.assign()`](http://www.2ality.com/2014/01/object-assign.html) ponyfill 4 | 5 | > Ponyfill: A polyfill that doesn't overwrite the native method 6 | 7 | 8 | ## Install 9 | 10 | ```sh 11 | $ npm install --save object-assign 12 | ``` 13 | 14 | 15 | ## Usage 16 | 17 | ```js 18 | var objectAssign = require('object-assign'); 19 | 20 | objectAssign({foo: 0}, {bar: 1}); 21 | //=> {foo: 0, bar: 1} 22 | 23 | // multiple sources 24 | objectAssign({foo: 0}, {bar: 1}, {baz: 2}); 25 | //=> {foo: 0, bar: 1, baz: 2} 26 | 27 | // overwrites equal keys 28 | objectAssign({foo: 0}, {foo: 1}, {foo: 2}); 29 | //=> {foo: 2} 30 | 31 | // ignores null and undefined sources 32 | objectAssign({foo: 0}, null, {bar: 1}, undefined); 33 | //=> {foo: 0, bar: 1} 34 | ``` 35 | 36 | 37 | ## API 38 | 39 | ### objectAssign(target, source, [source, ...]) 40 | 41 | Assigns enumerable own properties of `source` objects to the `target` object and returns the `target` object. Additional `source` objects will overwrite previous ones. 42 | 43 | 44 | ## Resources 45 | 46 | - [ES6 spec - Object.assign](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign) 47 | 48 | 49 | ## License 50 | 51 | MIT © [Sindre Sorhus](http://sindresorhus.com) 52 | -------------------------------------------------------------------------------- /test/helpers/logHook.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var log = [], 4 | _stream = process.stdout, 5 | old_write = _stream.write; // Reference default write method 6 | 7 | 8 | /** 9 | * Hooking function for console.log interception 10 | * 11 | * Methods: 12 | * .on() 13 | * .off() 14 | * 15 | * Example: 16 | * var logHook = require('logHook.js'), 17 | * _logs; 18 | * 19 | * // turning hook on 20 | * logHook.on(); 21 | * 22 | * // log test message 23 | * console.log('test message'); 24 | * 25 | * // turning hook off, now we have variable _logs === ['test message'] 26 | * _logs = logHook.off(); 27 | * 28 | * 29 | * @return {function} 30 | */ 31 | module.exports = { 32 | /** 33 | * Turns 'console.log' hook on and push all the incomming lines from 'process.stdout' into 'log' variable 34 | * @return {Void} 35 | */ 36 | on: function() { 37 | log = []; 38 | 39 | /* _stream now write with our shiny function */ 40 | _stream.write = function(string) { 41 | log.push(string.replace(/\n$/, '')); 42 | }; 43 | }, 44 | /** 45 | * Turns 'console.log' hook off and returns array with 'process.stdout' lines 46 | * @return {Array} Module local 'log' variable 47 | */ 48 | off: function() { 49 | /* reset to the default write method */ 50 | _stream.write = old_write; 51 | return log; 52 | } 53 | } -------------------------------------------------------------------------------- /new_project/client/code/app/app.demo.coffee: -------------------------------------------------------------------------------- 1 | ### QUICK CHAT DEMO #### 2 | 3 | # Delete this file once you've seen how the demo works 4 | 5 | # Listen out for newMessage events coming from the server 6 | ss.event.on 'newMessage', (message) -> 7 | 8 | # Example of using the Hogan Template in client/templates/chat/message.jade to generate HTML for each message 9 | html = ss.tmpl['chat-message'].render({message: message, time: -> timestamp() }) 10 | 11 | # Append it to the #chatlog div and show effect 12 | $(html).hide().appendTo('#chatlog').slideDown() 13 | 14 | 15 | # Show the chat form and bind to the submit action 16 | $('#demo').on 'submit', -> 17 | 18 | # Grab the message from the text box 19 | text = $('#myMessage').val() 20 | 21 | # Call the 'send' funtion (below) to ensure it's valid before sending to the server 22 | exports.send text, (success) -> 23 | if success 24 | $('#myMessage').val('') # clear text box 25 | else 26 | alert('Oops! Unable to send message') 27 | 28 | 29 | # Demonstrates sharing code between modules by exporting function 30 | exports.send = (text, cb) -> 31 | if valid(text) 32 | ss.rpc('demo.sendMessage', text, cb) 33 | else 34 | cb(false) 35 | 36 | 37 | # Private functions 38 | 39 | timestamp = -> 40 | d = new Date() 41 | d.getHours() + ':' + pad2(d.getMinutes()) + ':' + pad2(d.getSeconds()) 42 | 43 | pad2 = (number) -> 44 | (if number < 10 then '0' else '') + number 45 | 46 | valid = (text) -> 47 | text && text.length > 0 -------------------------------------------------------------------------------- /lib/request/middleware/internal.js: -------------------------------------------------------------------------------- 1 | // Internal Request Middleware 2 | // --------------------------- 3 | // Internal middleware occupies the top-level namespace, i.e. does not contain any dots 4 | 'use strict'; 5 | 6 | var colors = require('colors/safe'); 7 | 8 | module.exports = function(ss) { 9 | return { 10 | debug: function(color) { 11 | return function(request, response, next) { 12 | ss.log.info(colors[color || 'yellow']('Debug incoming message >>\n'), request); 13 | return next(); 14 | }; 15 | }, 16 | session: function(options) { 17 | if (!options) { 18 | options = {}; 19 | } 20 | return function(request, response, next) { 21 | if (request.sessionId) { 22 | return ss.session.find(request.sessionId, request.socketId, function(thisSession) { 23 | request.session = thisSession; 24 | if (options.debug) { 25 | ss.log.info(colors.yellow('Debug session >>\n'), thisSession); 26 | } 27 | if (thisSession) { 28 | return next(); 29 | } else { 30 | return ss.log.error(('! Error: Session ID ' + request.sessionId + 31 | ' not found. Use Redis to persist sessions between server restarts. Terminating incoming request').red); 32 | } 33 | }); 34 | } else { 35 | throw new Error('Cannot load session. Request does not contain a sessionId'); 36 | } 37 | }; 38 | } 39 | }; 40 | }; 41 | -------------------------------------------------------------------------------- /docs/partials/api/client.task.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

task 2 |
service in module client 3 | 4 |
5 |

6 |

Usage

7 |
task(client, name, dependents, fn);
8 |

Parameters

ParamTypeDetails
clientstring

Name of client

9 |
namestring

Name of task

10 |
dependentsstring

Array of other client tasks to execute

11 |
fnfunction

Task execution

12 |
13 |
14 | -------------------------------------------------------------------------------- /lib/tasks/live_reload.js: -------------------------------------------------------------------------------- 1 | // Live Reload 2 | // ----------- 3 | // Detects changes in client files and sends an event to connected browsers instructing them to refresh the page 4 | 'use strict'; 5 | 6 | require('colors'); 7 | 8 | var watcher, 9 | debug = require('debug')('socketstream:reload'); 10 | 11 | module.exports = function(ss, options) { 12 | 13 | var pathlib = require('path'), 14 | chokidar = ss.require('chokidar'); 15 | 16 | var watchDirs = (function() { 17 | var _i, _len, _ref, _results; 18 | _ref = options.liveReload; 19 | _results = []; 20 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 21 | var dir = _ref[_i]; 22 | _results.push(pathlib.join(ss.root, options.dirs[dir] || dir)); 23 | } 24 | return _results; 25 | })(); 26 | 27 | watcher = chokidar.watch(watchDirs, { 28 | ignoreInitial: true, 29 | ignored: /(\/\.|~$)/ 30 | }); 31 | watcher.on('add', function(path) { 32 | debug('added: %s',path); 33 | return ss.livereload.added(path); 34 | }); 35 | watcher.on('change', function(path) { 36 | debug('added: %s',path); 37 | return ss.livereload.changed(path); 38 | }); 39 | watcher.on('unlink', function(path) { 40 | debug('added: %s',path); 41 | return ss.livereload.removed(path); 42 | }); 43 | watcher.on('error', function(error) { 44 | return ss.log.error('✎'.red, ('Error: ' + error).red); 45 | }); 46 | }; 47 | 48 | module.exports.unload = function() { 49 | if (watcher) { 50 | watcher.close(); 51 | watcher = null; 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /test/unit/client/formatters/css.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'), 4 | ss = require( '../../../../lib/socketstream'), 5 | fixtures = require('../../../fixtures'); 6 | 7 | 8 | describe('css formatter', function () { 9 | 10 | beforeEach(function() { 11 | ss.client.reset(); 12 | ss.client.load(); 13 | }); 14 | 15 | describe('#init', function () { 16 | 17 | it('should return an object describing what file extensions the formatter handles', function() { 18 | 19 | var formatters = ss.api.client.formatters; 20 | 21 | formatters.css.should.be.type('object'); 22 | formatters.css.extensions.should.eql(['css']); 23 | }); 24 | 25 | 26 | it('should return an object describing what asset and content types that the formatter handles',function() { 27 | 28 | var formatters = ss.api.client.formatters; 29 | 30 | formatters.css.should.be.type('object'); 31 | formatters.css.assetType.should.equal('css'); 32 | formatters.css.contentType.should.equal('text/css'); 33 | }); 34 | }); 35 | 36 | describe('#compile', function () { 37 | 38 | it('should return the CSS file content as is', function() { 39 | 40 | var concrete = ss.api.client.formatters.css; 41 | var output; 42 | concrete.call(path.join(fixtures.project,'client/abc/style.css'),{},function(out) { 43 | output = out; 44 | out.should.be.equal(fixtures.expected_css); 45 | },function(err) { 46 | should(err).be.equal(undefined); 47 | }); 48 | }); 49 | }); 50 | 51 | 52 | }); 53 | -------------------------------------------------------------------------------- /test/unit/client/bundler/custom.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'), 4 | ss = require( '../../../fixtures/socketstream'), 5 | options = ss.client.options, 6 | defineAbcClient = require('../abcClient'); 7 | 8 | describe('custom bundler', function () { 9 | 10 | describe('system modules', function() { 11 | 12 | beforeEach(function() { 13 | 14 | ss.client.reset(); 15 | ss.client.set({liveReload:false}); 16 | }); 17 | 18 | afterEach(function() { 19 | ss.client.forget(); 20 | }); 21 | 22 | 23 | it('should be rendered correctly as htmlTags', function() { 24 | 25 | 26 | 27 | var client = defineAbcClient({ 28 | custom: './customBundler' 29 | },function() { 30 | }); 31 | 32 | var bundler = ss.api.bundler.get(client); 33 | var tags = bundler.htmlTags('js',false); 34 | 35 | tags[0].should.equal(''); 36 | tags[1].should.equal(''); 37 | tags[2].should.equal(''); // added this to pass test, not sure why it differs 38 | tags[3].should.equal(''); 39 | }); 40 | }); 41 | 42 | describe('start code',function() { 43 | 44 | it('should be rendered correctly as htmlTags'); 45 | 46 | }); 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /new_project/client/css/app.demo.styl: -------------------------------------------------------------------------------- 1 | // Example Stylus file 2 | // 3 | // Stylus docs: http://learnboost.github.com/stylus/docs/js.html 4 | // Nib docs: http://visionmedia.github.com/nib 5 | 6 | @import 'nib' 7 | 8 | // Main 9 | 10 | body, html 11 | min-height 100% 12 | 13 | body 14 | font normal 1em sans-serif 15 | background linear-gradient(top, #eee, #fff) 16 | text-align center 17 | 18 | p 19 | margin-bottom 1em 20 | 21 | a 22 | color #000a68 23 | 24 | h1 25 | font-size 1.5em 26 | font-weight normal 27 | margin 1.5em 28 | color #333 29 | text-shadow 1px 1px 2px white 30 | 31 | #content 32 | padding 50px 33 | 34 | 35 | // Quick Chat Demo 36 | 37 | #demo 38 | border 1px solid #ccc 39 | width 700px 40 | text-align left 41 | margin 50px auto 42 | padding 15px 43 | border-radius 5px 44 | h3 45 | font-size 1.0em 46 | font-weight normal 47 | h5 48 | padding 5px 0 49 | color #666 50 | font-weight normal 51 | font-size 0.8em 52 | #myMessage, #chatlog 53 | border none 54 | box-shadow inset 0 0 2px #777 55 | border-radius 5px 56 | #myMessage 57 | width 690px 58 | padding 5px 59 | #chatlog 60 | width 700px 61 | background-color white 62 | margin 5px 0 63 | p 64 | font-size 0.9em 65 | color #000a68 66 | padding 3px 9px 67 | margin 0 68 | p:first-child 69 | padding-top 9px 70 | p:last-child 71 | padding-bottom 9px 72 | span.time 73 | color #666 74 | float right 75 | width 70px 76 | text-align right 77 | font-size 0.7em 78 | -------------------------------------------------------------------------------- /docs/partials/api/ss.add.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

add 2 |
service in module ss 3 | 4 |
5 |

6 |

Description

7 |

Call from your app to safely extend the 'ss' internal API object passed through to your /server code

8 |
9 |

Usage

10 |
new add(name, fn);
11 |

Parameters

ParamTypeDetails
namestring
    12 |
  • Key in the ss API.
  • 13 |
14 |
fnfunctionnumberbooleanstring
    15 |
  • value or function
  • 16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/serving_http_resources.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Serving HTTP Resources 3 | 4 | @description 5 | # Serving HTTP Resources 6 | 7 | With Mobile phones being the new king of personal communication it is crucial for your website to work more like 8 | and application and less like a centralised portal. It is crucial to work in an online/offline world. 9 | 10 | SocketStream has until now had an event based Router. In 0.5 this is being replaced. Routing will come back in some other form in 1.0 11 | 12 | The aim is to push assets and content to edge caches and ideally directly onto the client devices. This means that the content of your 13 | application must be identified so it can be push out before it is needed, so the traditional Connect/Express model pulls you in the wrong direction. 14 | 15 | That being said the paradigm of defining REST resources is a very good one and fits into where the we is and will be going with HTTP version 2. 16 | So you should continue to define your content with URL semantics. Go ahead and base your implementation on Connect/Express middleware. 17 | We've prepared some examples for you for 18 | [Express](https://github.com/socketstream/ss-examples/tree/master/express-4-and-js) and 19 | [Connect](https://github.com/socketstream/ss-examples/tree/master/connect-and-engineio). 20 | 21 | In the future Express apps will be extended with semantics for real-time endpoints. Permissions will be span both HTTP, streaming and 22 | client side semantics. 23 | 24 | For this reason the use of middlewares are likely to change. `ss.http.middleware` still mostly works but is deprecated. 25 | -------------------------------------------------------------------------------- /test/fixtures/project/client/code/main/demo.coffee: -------------------------------------------------------------------------------- 1 | ### QUICK CHAT DEMO #### 2 | 3 | # Delete this file once you've seen how the demo works 4 | # Note the final version of SocketStream 0.3 will create a 'bare-bones' new project by default 5 | # This tutorial/demo and our recommended tech stack will be installed with the 'socketstream new -r' option 6 | 7 | 8 | # Example of using a module 9 | message = require('message') 10 | 11 | 12 | # Listen for incoming messages and append them to the screen 13 | ss.event.on 'newMessage', (message) -> 14 | 15 | # Example of using the Hogan Template in client/templates/chat/message.jade to generate HTML for each message 16 | html = HT['chat-message'].render({message: message, time: -> timestamp() }) 17 | 18 | # Append it to the #chatlog div and show effect 19 | $(html).hide().appendTo('#chatlog').slideDown() 20 | 21 | 22 | # Wait for connection to the server 23 | SocketStream.event.on 'ready', -> 24 | 25 | # Show the chat form and bind to the submit action 26 | $('#demo').on 'submit', -> 27 | 28 | # Grab the message from the text box 29 | text = $('#myMessage').val() 30 | 31 | # Use the module to ensure it's valid before sending to the server 32 | message.send text, (success) -> 33 | if success 34 | $('#myMessage').val('') # clear text box 35 | else 36 | alert('Oops! Unable to send message') 37 | 38 | 39 | # Private functions 40 | 41 | timestamp = -> 42 | d = new Date() 43 | d.getHours() + ':' + pad2(d.getMinutes()) + ':' + pad2(d.getSeconds()) 44 | 45 | pad2 = (number) -> 46 | (if number < 10 then '0' else '') + number -------------------------------------------------------------------------------- /docs/partials/tutorials/client_side_development.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

Client-Side Development

6 |

Each view is served with a separate bundle of assets. A HTML, JS and CSS file makes up the view.

7 |

The production form will,

8 |
    9 |
  • be saved in the assets (configurable) directory and served under the /assets path.
  • 10 |
  • Have the same path and name
  • 11 |
  • have an extending that determines the type
  • 12 |
  • Follow the form assets/<name>/<id>.<type>.
  • 13 |
14 |

Each of the entries that make of the bundles are served separately to the browser while developing.

15 |
    16 |
  • The base path is the same as in production
  • 17 |
  • Additional identification will be passed with a query string
  • 18 |
  • The client and bundler is determined by the name part of the path.
  • 19 |
  • A parameter determines the entry (_,loader,mod,start)
  • 20 |
21 |

The URL pattern is http:///assets/<name>/<id>.<type>?_=<entry_path>.

22 |

Bundlers generally work within the client directory to reduce the amount of files to watch. The path 23 | of entries is normally resolved relative to the client directory.

24 |
25 | -------------------------------------------------------------------------------- /new_project/client/css/app.demo.css: -------------------------------------------------------------------------------- 1 | /* Example CSS file */ 2 | 3 | /* Main */ 4 | 5 | body, 6 | html { 7 | min-height: 100%; 8 | } 9 | body { 10 | font: normal 1em sans-serif; 11 | background: linear-gradient(top, #eeeeee, #ffffff); 12 | text-align: center; 13 | } 14 | p { 15 | margin-bottom: 1em; 16 | } 17 | a { 18 | color: #000a68; 19 | } 20 | h1 { 21 | font-size: 1.5em; 22 | font-weight: normal; 23 | margin: 1.5em; 24 | color: #333; 25 | text-shadow: 1px 1px 2px white; 26 | } 27 | #content { 28 | padding: 50px; 29 | } 30 | 31 | /* Quick Chat Demo */ 32 | 33 | #demo { 34 | border: 1px solid #ccc; 35 | width: 700px; 36 | text-align: left; 37 | margin: 50px auto; 38 | padding: 15px; 39 | border-radius: 5px; 40 | } 41 | #demo h3 { 42 | font-size: 1.0em; 43 | font-weight: normal; 44 | } 45 | #demo h5 { 46 | padding: 5px 0; 47 | color: #666; 48 | font-weight: normal; 49 | font-size: 0.8em; 50 | } 51 | #demo #myMessage, 52 | #demo #chatlog { 53 | border: none; 54 | box-shadow: inset 0 0 2px #777; 55 | border-radius: 5px; 56 | } 57 | #demo #myMessage { 58 | width: 690px; 59 | padding: 5px; 60 | } 61 | #demo #chatlog { 62 | width: 700px; 63 | background-color: white; 64 | margin: 5px 0; 65 | } 66 | #demo #chatlog p { 67 | font-size: 0.9em; 68 | color: #000a68; 69 | padding: 3px 9px; 70 | margin: 0; 71 | } 72 | #demo #chatlog p:first-child { 73 | padding-top: 9px; 74 | } 75 | #demo #chatlog p:last-child { 76 | padding-bottom: 9px; 77 | } 78 | #demo #chatlog span.time { 79 | color: #666; 80 | float: right; 81 | width: 70px; 82 | text-align: right; 83 | font-size: 0.7em; 84 | } 85 | -------------------------------------------------------------------------------- /test/fixtures/project/node_modules/mock-socket/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mock-socket", 3 | "version": "0.0.1", 4 | "description": "mock-socket", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/sindresorhus/object-assign.git" 9 | }, 10 | "author": { 11 | "name": "Sindre Sorhus", 12 | "email": "sindresorhus@gmail.com", 13 | "url": "http://sindresorhus.com" 14 | }, 15 | "engines": { 16 | "node": ">=0.10.0" 17 | }, 18 | "scripts": { 19 | "test": "xo && mocha", 20 | "bench": "matcha bench.js" 21 | }, 22 | "files": [ 23 | "index.js" 24 | ], 25 | "keywords": [ 26 | ], 27 | "gitHead": "b0c40d37cbc43e89ad3326a9bad4c6b3133ba6d3", 28 | "bugs": { 29 | "url": "https://github.com/sindresorhus/object-assign/issues" 30 | }, 31 | "homepage": "https://github.com/sindresorhus/object-assign#readme", 32 | "_id": "object-assign@4.0.1", 33 | "_shasum": "99504456c3598b5cad4fc59c26e8a9bb107fe0bd", 34 | "_from": "object-assign@*", 35 | "_npmVersion": "2.13.3", 36 | "_nodeVersion": "3.0.0", 37 | "_npmUser": { 38 | "name": "sindresorhus", 39 | "email": "sindresorhus@gmail.com" 40 | }, 41 | "dist": { 42 | "shasum": "99504456c3598b5cad4fc59c26e8a9bb107fe0bd", 43 | "tarball": "http://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz" 44 | }, 45 | "maintainers": [ 46 | { 47 | "name": "sindresorhus", 48 | "email": "sindresorhus@gmail.com" 49 | } 50 | ], 51 | "directories": {}, 52 | "_resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz", 53 | "readme": "ERROR: No README data found!" 54 | } 55 | -------------------------------------------------------------------------------- /lib/client/view.js: -------------------------------------------------------------------------------- 1 | // Client Views 2 | // ------------ 3 | // Generates HTML output for each single-page view 4 | 'use strict'; 5 | 6 | module.exports = function(ss, client, options, cb) { 7 | var templateEngine = require('./template_engine')(ss, options), //TODO why not use the one in client/index ? 8 | bundler = ss.bundler.get(client); 9 | 10 | function templates() { 11 | var files = bundler.entries('tmpl'), 12 | output = []; 13 | templateEngine.generate(bundler, files, function(html) { 14 | return output.push(html); 15 | }); 16 | return output; 17 | } 18 | 19 | function headers() { 20 | // Return an array of headers. Order is important! 21 | var output = []; 22 | 23 | // Send all CSS 24 | if (client.includes.css) { 25 | output = output.concat( bundler.htmlTags('css',!!client.pack) ); 26 | } 27 | 28 | // Send Application Code 29 | output = output.concat( bundler.htmlTags('js',!!client.pack) ); 30 | 31 | return output; 32 | } 33 | 34 | // Add links to CSS and JS files 35 | var includes = headers().concat(templates()); 36 | 37 | // In future constants might be placed at beginning of headers 38 | var start = bundler.htmlTags('start'); 39 | if (options.startInBundle) { 40 | includes = includes.concat(start); 41 | start = []; 42 | } 43 | 44 | // Output HTML 45 | var htmlOptions = { 46 | constants: bundler.constants(), 47 | locals: bundler.locals(), 48 | headers: includes.join(''), 49 | tail: start.join(''), 50 | compress: options.packedAssets, 51 | filename: client.paths.view 52 | }; 53 | return bundler.asset(bundler.entryFor('html',client.paths.view), htmlOptions, cb); 54 | }; 55 | -------------------------------------------------------------------------------- /test/helpers/uncache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | 5 | // These are helper methods extracted from here: 6 | // http://stackoverflow.com/questions/9210542/node-js-require-cache-possible-to-invalidate 7 | 8 | 9 | 10 | /** 11 | * Removes a module from the cache 12 | * @param {String} moduleName the path of the module 13 | * @return {Void} 14 | */ 15 | require.uncache = function (moduleName) { 16 | // Run over the cache looking for the files 17 | // loaded by the specified module name 18 | require.searchCache(moduleName, function (mod) { 19 | delete require.cache[mod.id]; 20 | }); 21 | }; 22 | 23 | 24 | 25 | /** 26 | * Runs over the cache to search for all the cached 27 | * files 28 | * @param {String} moduleName the path of the module 29 | * @param {Function} next callback 30 | * @return {Void} 31 | */ 32 | require.searchCache = function (moduleName, callback) { 33 | // Resolve the module identified by the specified name 34 | var mod = require.resolve(moduleName); 35 | 36 | // Check if the module has been resolved and found within 37 | // the cache 38 | if (mod && ((mod = require.cache[mod]) !== undefined)) { 39 | // Recursively go over the results 40 | (function run(mod) { 41 | // Go over each of the module's children and 42 | // run over it 43 | mod.children.forEach(function (child) { 44 | run(child); 45 | }); 46 | 47 | // Call the specified callback providing the 48 | // found module 49 | callback(mod); 50 | })(mod); 51 | } 52 | }; 53 | 54 | 55 | 56 | /** 57 | * Expose the Public API 58 | */ 59 | module.exports = require; -------------------------------------------------------------------------------- /docs/partials/api/client.define.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

define 2 |
service in module client 3 | 4 |
5 |

6 |

Description

7 |

Define a client view to serve.

8 |
ss.http.route('/my-view', function(req,res)
 9 |    res.serveClient('my-view');
10 | });
11 | 
12 |

Usage

13 |
define(name, paths);
14 |

Parameters

ParamTypeDetails
namestring

Logical name of the client

15 |
pathsobject

Paths of css, code, tmpl, view

16 |

Returns

{name: *

ClientDefinition} definition Client Definition

17 |
18 |
19 | -------------------------------------------------------------------------------- /new_project/client/css/app.demo.less: -------------------------------------------------------------------------------- 1 | // Example Less file 2 | // 3 | // Less docs: http://lesscss.org/ 4 | 5 | // Main 6 | 7 | body, html { 8 | min-height: 100%; 9 | } 10 | 11 | body { 12 | font: normal 1em sans-serif; 13 | background: linear-gradient(top, #eee, #fff); 14 | text-align: center; 15 | } 16 | p { 17 | margin-bottom: 1em; 18 | } 19 | 20 | a { 21 | color: #000a68; 22 | } 23 | 24 | h1 { 25 | font-size: 1.5em; 26 | font-weight: normal; 27 | margin: 1.5em; 28 | color: #333; 29 | text-shadow: 1px 1px 2px white; 30 | } 31 | 32 | #content { 33 | padding: 50px; 34 | } 35 | 36 | // Quick Chat Demo 37 | 38 | #demo { 39 | border: 1px solid #ccc; 40 | width: 700px; 41 | text-align: left; 42 | margin: 50px auto; 43 | padding: 15px; 44 | border-radius: 5px; 45 | h3 { 46 | font-size: 1.0em; 47 | font-weight: normal; 48 | } 49 | h5 { 50 | padding: 5px 0; 51 | color: #666; 52 | font-weight: normal; 53 | font-size: 0.8em; 54 | } 55 | #myMessage, #chatlog { 56 | border: none; 57 | box-shadow: inset 0 0 2px #777; 58 | border-radius: 5px; 59 | } 60 | #myMessage { 61 | width: 690px; 62 | padding: 5px; 63 | } 64 | #chatlog { 65 | width: 700px; 66 | background-color: white; 67 | margin: 5px 0; 68 | p { 69 | font-size: 0.9em; 70 | color: #000a68; 71 | padding: 3px 9px; 72 | margin: 0; 73 | &:first-child { 74 | padding-top: 9px; 75 | } 76 | &:last-child { 77 | padding-bottom: 9px; 78 | } 79 | } 80 | span.time { 81 | color: #666; 82 | float: right; 83 | width: 70px; 84 | text-align: right; 85 | font-size: 0.7em; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/request/responders/rpc/client.js: -------------------------------------------------------------------------------- 1 | // Tell's the browser how to respond to incoming 'rpc' messages 2 | 'use strict'; 3 | 4 | var cbStack, defaultCallback, numRequests, ss; 5 | 6 | ss = require('socketstream'); 7 | 8 | numRequests = 0; 9 | 10 | cbStack = {}; 11 | 12 | // Default callback 13 | defaultCallback = function(x) { 14 | return console.log(x); 15 | }; 16 | 17 | // SENDING 18 | 19 | module.exports = function(responderId, config, send) { 20 | ss.registerApi('rpc', function() { 21 | var args, cb, lastArg, msg, obj; 22 | args = Array.prototype.slice.call(arguments); 23 | 24 | // Prepare message 25 | obj = {}; 26 | obj.m = args[0]; // method 27 | obj.id = ++numRequests; // unique request id 28 | 29 | // Callback 30 | lastArg = args[args.length - 1]; 31 | if (typeof lastArg === 'function') { 32 | obj.p = args.slice(1, args.length - 1); 33 | cb = lastArg; 34 | } else { 35 | obj.p = args.slice(1); 36 | cb = defaultCallback; 37 | } 38 | 39 | // Add callback to stack 40 | cbStack[obj.id] = cb; 41 | 42 | // Convert to JSON 43 | msg = JSON.stringify(obj); 44 | 45 | // Send it! 46 | send(msg); 47 | 48 | // Always return undefined 49 | return void 0; 50 | }); 51 | 52 | // RECEIVING 53 | 54 | return ss.message.on(responderId, function(msg) { 55 | var cb, obj; 56 | obj = JSON.parse(msg); 57 | 58 | // If callback 59 | if (obj.id && (cb = cbStack[obj.id])) { 60 | if (obj.e) { 61 | console.error('SocketStream RPC server error:', obj.e.message); 62 | } else { 63 | cb.apply(cb, obj.p); 64 | } 65 | return delete cbStack[obj.id]; 66 | } 67 | }); 68 | }; 69 | -------------------------------------------------------------------------------- /new_project/client/code/app/app.demo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* QUICK CHAT DEMO */ 3 | 4 | // Delete this file once you've seen how the demo works 5 | 6 | // Listen out for newMessage events coming from the server 7 | ss.event.on('newMessage', function(message) { 8 | 9 | // Example of using the Hogan Template in client/templates/chat/message.jade to generate HTML for each message 10 | var html = ss.tmpl['chat-message'].render({ 11 | message: message, 12 | time: function() { return timestamp(); } 13 | }); 14 | 15 | // Append it to the #chatlog div and show effect 16 | return $(html).hide().appendTo('#chatlog').slideDown(); 17 | }); 18 | 19 | // Show the chat form and bind to the submit action 20 | $('#demo').on('submit', function() { 21 | 22 | // Grab the message from the text box 23 | var text = $('#myMessage').val(); 24 | 25 | // Call the 'send' funtion (below) to ensure it's valid before sending to the server 26 | return exports.send(text, function(success) { 27 | if (success) { 28 | return $('#myMessage').val(''); 29 | } else { 30 | return alert('Oops! Unable to send message'); 31 | } 32 | }); 33 | }); 34 | 35 | // Demonstrates sharing code between modules by exporting function 36 | exports.send = function(text, cb) { 37 | if (valid(text)) { 38 | return ss.rpc('demo.sendMessage', text, cb); 39 | } else { 40 | return cb(false); 41 | } 42 | }; 43 | 44 | 45 | // Private functions 46 | 47 | function timestamp() { 48 | var d = new Date(); 49 | return d.getHours() + ':' + pad2(d.getMinutes()) + ':' + pad2(d.getSeconds()); 50 | } 51 | 52 | function pad2(number) { 53 | return (number < 10 ? '0' : '') + number; 54 | } 55 | 56 | function valid(text) { 57 | return text && text.length > 0; 58 | } -------------------------------------------------------------------------------- /test/fixtures/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs-extra'), 4 | path = require('path'); 5 | 6 | exports.project = path.join(__dirname, 'project'); 7 | exports.files = path.join(__dirname, 'files'); 8 | exports.readDirSync = path.join(__dirname, 'readDirSync'); 9 | 10 | exports.setProject = function(name) { 11 | var ss = require('../../lib/socketstream'); 12 | ss.root = ss.api.root = path.join(__dirname, name); 13 | }; 14 | 15 | exports.socketstreamProject = function(name) { 16 | var ss = require('../../lib/socketstream'); 17 | ss.root = ss.api.root = path.join(__dirname, name || 'project'); 18 | return ss; 19 | }; 20 | 21 | exports.reset = function(done) { 22 | // generated abc client assets 23 | fs.emptyDir(path.join(__dirname,'project/client/static/assets/abc'),function() { 24 | if (done) { 25 | done(); 26 | } 27 | }); 28 | }; 29 | 30 | exports.cleanup = function(done) { 31 | fs.emptyDir(path.join(__dirname, 'project','client','static','assets'), done); 32 | }; 33 | 34 | exports.setAbcPreviousAbcAssets = function() { 35 | var id = '1234567890'; 36 | fs.closeSync(fs.openSync(path.join(__dirname,'project/client/static/assets/abc',id+'.css'),'w')); 37 | fs.closeSync(fs.openSync(path.join(__dirname,'project/client/static/assets/abc',id+'.js'),'w')); 38 | fs.closeSync(fs.openSync(path.join(__dirname,'project/client/static/assets/abc',id+'.html'),'w')); 39 | }; 40 | 41 | exports.expected_css = '/* style.css */\nbody {color:red;}\n'; 42 | exports.expected_css_packed = 'body{color:red;}\n'; 43 | exports.expected_html_packed = fs.readFileSync(path.join(__dirname,'project/client/abc/expected.html'),'utf-8'); 44 | exports.expected_js_packed = fs.readFileSync(path.join(__dirname,'project/client/abc/expected.min.js'),'utf-8'); 45 | -------------------------------------------------------------------------------- /docs/css/prettify.css: -------------------------------------------------------------------------------- 1 | .pln { color: #000 } /* plain text */ 2 | 3 | @media screen { 4 | .str { color: #080 } /* string content */ 5 | .kwd { color: #008 } /* a keyword */ 6 | .com { color: #800 } /* a comment */ 7 | .typ { color: #606 } /* a type name */ 8 | .lit { color: #066 } /* a literal value */ 9 | /* punctuation, lisp open bracket, lisp close bracket */ 10 | .pun, .opn, .clo { color: #660 } 11 | .tag { color: #008 } /* a markup tag name */ 12 | .atn { color: #606 } /* a markup attribute name */ 13 | .atv { color: #080 } /* a markup attribute value */ 14 | .dec, .var { color: #606 } /* a declaration; a variable name */ 15 | .fun { color: red } /* a function name */ 16 | } 17 | 18 | /* Use higher contrast and text-weight for printable form. */ 19 | @media print, projection { 20 | .str { color: #060 } 21 | .kwd { color: #006; font-weight: bold } 22 | .com { color: #600; font-style: italic } 23 | .typ { color: #404; font-weight: bold } 24 | .lit { color: #044 } 25 | .pun, .opn, .clo { color: #440 } 26 | .tag { color: #006; font-weight: bold } 27 | .atn { color: #404 } 28 | .atv { color: #060 } 29 | } 30 | 31 | pre.prettyprint { 32 | padding: 8px; 33 | background-color: #f7f7f9; 34 | border: 1px solid #e1e1e8; 35 | } 36 | pre.prettyprint.linenums { 37 | -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 38 | -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 39 | box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 40 | } 41 | ol.linenums { 42 | margin: 0 0 0 33px; /* IE indents via margin-left */ 43 | } 44 | ol.linenums li { 45 | padding-left: 12px; 46 | font-size:12px; 47 | color: #bebec5; 48 | line-height: 18px; 49 | text-shadow: 0 1px 0 #fff; 50 | list-style-type:decimal!important; 51 | } 52 | -------------------------------------------------------------------------------- /docs/partials/tutorials/url_scheme.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

URL Scheme

6 |

The common URL for a view is its name at the root level, but you can choose whatever you will calling serveClient(..).

7 |

Assets

8 |

The contents of the client assets directory will be served under /assets.

9 |

When views are packed for production they are saved under the client assets directory. This will change in the future 10 | to make relative URLs work the same in development and production.

11 |

Middleware

12 |

At development time middleware is added to serve HTML, JS and CSS on the fly.

13 |

Serving CSS

14 |

CSS files are served under /assets//123.css in production. When served ad hoc in development, and on-demand in 15 | production all CSS must be served on the same level and ideally in an equivalent URL.

16 |

JS Module Paths

17 |

On Demand Loading

18 |

The current on-demand fetching of JS is handled by middleware. It should be possible to do it using static files.

19 |

In production it would make sense to support a path like /assets/require/...

20 |

We will have to consider whether all client code is considered completely open, or only partially. Should all client 21 | modules be exported in minified form, or only those in a whitelist.

22 |
23 | -------------------------------------------------------------------------------- /docs/partials/api/client.formatters.formatters.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

formatters 2 |
service in module client 3 | 4 |
5 |

6 |

Description

7 |

This is for the module returned by require('socketstream').client.formatters.

8 |
9 |

Methods

10 |
  • add(nameOrModule, config)

    11 |
    Parameters
    ParamTypeDetails
    nameOrModulestringobject

    the formatter object or name

    12 |
    configobject

    parameters configuring the formatter

    13 |
    14 |
  • 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /test/fixtures/project/node_modules/socketstream-addon/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socketstream-addon", 3 | "version": "4.0.1", 4 | "description": "ES6 Object.assign() ponyfill", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/sindresorhus/object-assign.git" 9 | }, 10 | "author": { 11 | "name": "Sindre Sorhus", 12 | "email": "sindresorhus@gmail.com", 13 | "url": "http://sindresorhus.com" 14 | }, 15 | "engines": { 16 | "node": ">=0.10.0" 17 | }, 18 | "scripts": { 19 | "test": "xo && mocha", 20 | "bench": "matcha bench.js" 21 | }, 22 | "files": [ 23 | "index.js" 24 | ], 25 | "keywords": [ 26 | ], 27 | "devDependencies": { 28 | "lodash": "^3.10.1", 29 | "matcha": "^0.6.0", 30 | "mocha": "*", 31 | "xo": "*" 32 | }, 33 | "gitHead": "b0c40d37cbc43e89ad3326a9bad4c6b3133ba6d3", 34 | "bugs": { 35 | "url": "https://github.com/sindresorhus/object-assign/issues" 36 | }, 37 | "homepage": "https://github.com/sindresorhus/object-assign#readme", 38 | "_id": "object-assign@4.0.1", 39 | "_shasum": "99504456c3598b5cad4fc59c26e8a9bb107fe0bd", 40 | "_from": "object-assign@*", 41 | "_npmVersion": "2.13.3", 42 | "_nodeVersion": "3.0.0", 43 | "_npmUser": { 44 | "name": "sindresorhus", 45 | "email": "sindresorhus@gmail.com" 46 | }, 47 | "dist": { 48 | "shasum": "99504456c3598b5cad4fc59c26e8a9bb107fe0bd", 49 | "tarball": "http://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz" 50 | }, 51 | "maintainers": [ 52 | { 53 | "name": "sindresorhus", 54 | "email": "sindresorhus@gmail.com" 55 | } 56 | ], 57 | "directories": {}, 58 | "_resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz", 59 | "readme": "ERROR: No README data found!" 60 | } 61 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/choosing_protocol.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Choosing a Protocol 3 | 4 | @description 5 | # Choosing a Protocol 6 | 7 | The state of web protocols is such that WebSockets are supported on perhaps 90% of the devices that reach your 8 | website. The first question you have to ask yourself is what to do about the last 10%. You can ignore them and 9 | configure SocketStream to only use WebSockets for streaming connections. This is an option that will be added 10 | in version `0.7` and be stable in `0.8`. 11 | 12 | Otherwise you can support 100% of devices with fallback protocols such as "engine.io" or "SockJS". They are both 13 | mature but with different approaches. SockJS tries to open a WebSocket and fall backs to various polling techniques 14 | if it cannot. Engine.io starts with polling and tries to upgrade ultimately to a WebSocket connection. So for 15 | 90% of devices SockJS will connect faster. 16 | 17 | However engine.io supports compression and binary data, so you may push more data to the client. This may not 18 | be relevant for your application, so both are good options. 19 | 20 | For streaming connections it is also important to consider Internet proxies and gateways. Many firewalls only let 21 | through HTTP and stops all other protocols. To support this you cannot use straight WebSockets as the fallback 22 | mechanisms must always be used. 23 | 24 | Over the next year HTTP version 2 will become supported in most web browsers and web servers. It allows some 25 | advanced data caching techniques that can be used to achieve Real-Time data streaming to clients over HTTP. 26 | 27 | Hopefully we will be able to extend SocketStream with the ability to stream over HTTP/2. 28 | 29 | Another interesting candidate is WebRTC which allows browsers to talk to each other. This might also be fitted 30 | into the Real-Time puzzle. 31 | -------------------------------------------------------------------------------- /lib/websocket/event_dispatcher.js: -------------------------------------------------------------------------------- 1 | // Websocket Event Dispatcher 2 | // -------------------------- 3 | // Delivers events to individual (or groups of) websocket IDs 4 | 'use strict'; 5 | 6 | var subscriptions = require('./subscriptions'); 7 | 8 | module.exports = function(eventTransport, wsTransport, emitter) { 9 | return eventTransport.listen(function(obj) { 10 | var send = wsTransport.event(); 11 | 12 | // Emit message to the event responder (always Responder ID 0) 13 | return emitter.emit('0', obj, {}, andThen()); 14 | 15 | function andThen() { 16 | switch (obj.t) { 17 | case 'all': 18 | return function(msg) { 19 | return send.all(msg); 20 | }; 21 | case 'socketId': 22 | return function(msg) { 23 | return send.socketId(obj.socketId, msg); 24 | }; 25 | case 'channel': 26 | return function(msg) { 27 | return sendToMultiple(send, msg, obj.channels, 'channel'); 28 | }; 29 | case 'user': 30 | return function(msg) { 31 | return sendToMultiple(send, msg, obj.users, 'user'); 32 | }; 33 | } 34 | } 35 | }); 36 | }; 37 | 38 | 39 | // Private 40 | 41 | // Attempt to send the event to the socket. If socket no longer exists, remove it from set 42 | function sendToMultiple(send, msg, destinations, type) { 43 | destinations = destinations instanceof Array && destinations || [destinations]; 44 | destinations.forEach(function(destination) { 45 | var set, socketIds; 46 | set = subscriptions[type]; 47 | if ((socketIds = set.members(destination))) { 48 | return socketIds.slice(0).forEach(function(socketId) { 49 | if (!send.socketId(socketId, msg, destination)) { 50 | return set.removeFromAll(socketId); 51 | } 52 | }); 53 | } 54 | }); 55 | return true; 56 | } 57 | -------------------------------------------------------------------------------- /test/unit/client/formatters/html.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'), 4 | ss = require( '../../../../lib/socketstream'), 5 | fixtures = require('../../../fixtures'); 6 | 7 | 8 | describe('html formatter', function () { 9 | 10 | beforeEach(function() { 11 | ss.client.reset(); 12 | ss.client.load(); 13 | }); 14 | 15 | describe('#init', function () { 16 | 17 | it('should return an object describing what file extensions the formatter handles', function() { 18 | 19 | var formatters = ss.api.client.formatters; 20 | 21 | formatters.html.should.be.type('object'); 22 | formatters.html.extensions.should.eql(['html']); 23 | }); 24 | 25 | 26 | it('should return an object describing what asset and content types that the formatter handles',function() { 27 | 28 | var formatters = ss.api.client.formatters; 29 | 30 | formatters.html.should.be.type('object'); 31 | formatters.html.assetType.should.equal('html'); 32 | formatters.html.contentType.should.equal('text/html'); 33 | }); 34 | 35 | 36 | describe('#compile', function () { 37 | 38 | it('should return the HTML file content as is', function() { 39 | 40 | var concrete = ss.api.client.formatters.html; 41 | var output; 42 | concrete.call(path.join(fixtures.project,'client/abc/ss.html'),{},function(out) { 43 | output = out; 44 | out.should.be.equal('\nABC\n

ABC

\n\n'); 45 | },function(err) { 46 | should(err).be.equal(undefined); 47 | }); 48 | }); 49 | 50 | describe('if header options are passed', function () { 51 | 52 | it('should replace any tags in the html with the value in the header options'); 53 | 54 | }); 55 | 56 | }); 57 | 58 | }); 59 | 60 | 61 | }); 62 | -------------------------------------------------------------------------------- /lib/websocket/index.js: -------------------------------------------------------------------------------- 1 | // Websocket Module 2 | // ---------------- 3 | // Handles everything to do with the websocket transport and message responders 4 | 'use strict'; 5 | 6 | var debug = require('debug')('socketstream'), 7 | EventEmitter2 = require('eventemitter2').EventEmitter2, 8 | emitter = new EventEmitter2({ 9 | wildcard: true 10 | }); 11 | 12 | module.exports = function(ss) { 13 | 14 | // Return API 15 | var transport = require('./transport')(ss, emitter); 16 | 17 | return { 18 | /** 19 | * @ngdoc service 20 | * @name http.index:index#listen 21 | * @methodOf http.index:index 22 | * @function 23 | * @description 24 | * Start the server listening to the port (same as Server.listen) 25 | */ 26 | listen: function(port,cb) { 27 | ss.load(); 28 | ss.log.info('Starting SocketStream %s in %s mode...'.green, ss.version, ss.env); 29 | if (Number(ss.version.split('.')[1]) % 2) { 30 | ss.log.info('This is an unstable version, some features will not be reliable'.yellow); 31 | } 32 | return transport.http.listen(port,function() { 33 | debug('started streaming server.'); 34 | if (cb) { 35 | return cb(); 36 | } 37 | }); 38 | }, 39 | 40 | transport: transport, 41 | unload: function() {}, 42 | load: function(responders, eventTransport) { 43 | var thisTransport = transport.load(); 44 | 45 | // Dispatch incoming events to websocket clients 46 | require('./event_dispatcher')(eventTransport, thisTransport, emitter); 47 | 48 | // Listen to incoming requests and invoke server.request 49 | for (var id in responders) { 50 | var responder = responders[id]; 51 | emitter.on(id, responder.interfaces.websocket); 52 | } 53 | 54 | // Return active WS transport 55 | return thisTransport; 56 | } 57 | }; 58 | }; 59 | -------------------------------------------------------------------------------- /docs/partials/tutorials/start_targets.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

Start Targets

6 |

When SocketStream is started in runs on an Orchestrator just like the build tool Gulp.

7 |

Note that this interface is experimental and may change

8 |
Starting the server
9 |

The default target will build assets and start a web server as needed. If other named targets 10 | are specified they will be started instead.

11 |

To start your socketstream project with different build targets specify them after the server 12 | object, or instead if you do not want to run the streaming server.

13 |
// Start web server
14 | var server = http.Server(ss.http.middleware);
15 | server.listen(3000);
16 | 
17 | // Start SocketStream
18 | ss.start(server,'my-task','serve');
19 | 
Default tasks
20 |

The default task depends on

21 |
    22 |
  • pack-all/pack-if-needed if using packed assets
  • 23 |
  • live-assets if not using packed assets
  • 24 |
  • live-reload if live reload is enabled (true by default)
  • 25 |
  • and serve-on-demand
  • 26 |
27 |
Hooking in
28 |

If you want to react when things happen around tasks you can register event handlers. 29 | The tasks are managed by Orchestrator which manages tasks in Gulp.

30 |

Register events on ss.api.orchestrator

31 |
32 | -------------------------------------------------------------------------------- /test/unit/cli/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | 5 | // Dependencies 6 | // 7 | var assert = require('assert'); 8 | var Gently = require('gently'); 9 | var generator = require('../../../lib/cli/generate.js'); 10 | var index = require('../../../lib/cli/index.js'); 11 | var gently = new Gently(); 12 | 13 | 14 | 15 | describe('/index.js', function () { 16 | 17 | 18 | 19 | describe('#process', function () { 20 | 21 | 22 | 23 | describe('when the 1st argument is n', function () { 24 | 25 | it('should call the generator with the program arguments', function (done) { 26 | var program = {args: ['n', 'testapp']}; 27 | gently.expect(generator, 'generate', function (receivedProgram) { 28 | assert.deepEqual(program,receivedProgram); 29 | done(); 30 | }); 31 | index.process(program); 32 | }); 33 | 34 | }); 35 | 36 | 37 | 38 | describe('when the 1st argument is new', function () { 39 | 40 | it('should call the generator with the program arguments', function (done) { 41 | var program = {args: ['new', 'testapp']}; 42 | gently.expect(generator, 'generate', function (receivedProgram) { 43 | assert.deepEqual(program,receivedProgram); 44 | done(); 45 | }); 46 | index.process(program); 47 | }); 48 | 49 | }); 50 | 51 | 52 | 53 | describe('when the 1st argument is neither "n" or "new"', function () { 54 | 55 | it('should inform the user on how to use the application', function (done) { 56 | var program = {args: ['create', 'testapp']}; 57 | gently.expect(console, 'log', function (string) { 58 | assert.deepEqual('Type "socketstream new " to create a new application',string); 59 | done(); 60 | }); 61 | index.process(program); 62 | }); 63 | 64 | }); 65 | 66 | 67 | 68 | }); 69 | 70 | 71 | 72 | }); 73 | -------------------------------------------------------------------------------- /docs/partials/tutorials/serving_http_resources.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

Serving HTTP Resources

6 |

With Mobile phones being the new king of personal communication it is crucial for your website to work more like 7 | and application and less like a centralised portal. It is crucial to work in an online/offline world.

8 |

SocketStream has until now had an event based Router. In 0.5 this is being replaced. Routing will come back in some other form in 1.0

9 |

The aim is to push assets and content to edge caches and ideally directly onto the client devices. This means that the content of your 10 | application must be identified so it can be push out before it is needed, so the traditional Connect/Express model pulls you in the wrong direction.

11 |

That being said the paradigm of defining REST resources is a very good one and fits into where the we is and will be going with HTTP version 2. 12 | So you should continue to define your content with URL semantics. Go ahead and base your implementation on Connect/Express middleware. 13 | We've prepared some examples for you for 14 | Express and 15 | Connect.

16 |

In the future Express apps will be extended with semantics for real-time endpoints. Permissions will be span both HTTP, streaming and 17 | client side semantics.

18 |

For this reason the use of middlewares are likely to change. ss.http.middleware still mostly works but is deprecated.

19 |
20 | -------------------------------------------------------------------------------- /test/unit/client/template_engines/default.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'), 4 | ss = require( '../../../../lib/socketstream'), 5 | bundlerMod = require('../../../../lib/client/bundler'), 6 | engineMod = require('../../../../lib/client/template_engine'), 7 | options = ss.client.options, 8 | fixtures = require('../../../fixtures'); 9 | 10 | 11 | describe('default template engine', function () { 12 | 13 | fixtures.setProject('project'); 14 | 15 | ss.api.bundler = bundlerMod(ss.api, options); 16 | 17 | engineMod(ss.api,options); 18 | 19 | beforeEach(function() { 20 | ss.client.reset(); 21 | ss.client.set({liveReload:false}); 22 | }); 23 | 24 | it('should output an inline template for use with jQuery or HoganJS', function(done) { 25 | 26 | ss.client.define('abc', { 27 | css: './abc/style.css', 28 | code: './abc/index.a', 29 | view: './abc/abc.html' 30 | }); 31 | 32 | ss.client.load(); 33 | 34 | var bundler = ss.api.bundler.get('abc'); 35 | 36 | var files = [ bundler.entryFor('tmpl','client/templates/1.html') ]; 37 | 38 | ss.client.templateEngine.generate(bundler, files, function(tag) { 39 | tag.should.be.equal(''); 40 | done(); 41 | }); 42 | 43 | }); 44 | 45 | it('should output an inline template for a subpath', function(done) { 46 | 47 | ss.client.define('abc', { 48 | css: './abc/style.css', 49 | code: './abc/index.a', 50 | view: './abc/abc.html' 51 | }); 52 | 53 | ss.client.load(); 54 | 55 | var bundler = ss.api.bundler.get('abc'); 56 | 57 | var files = [ bundler.entryFor('tmpl','client/templates/abc/1.html') ]; 58 | 59 | ss.client.templateEngine.generate(bundler, files, function(tag) { 60 | tag.should.be.equal(''); 61 | done(); 62 | }); 63 | 64 | }); 65 | 66 | 67 | }); 68 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/how_to_write_a_formatter.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name How to write a formatter 3 | 4 | @description 5 | # How to write a formatter ? 6 | 7 | Formatters can be written as a function returning an object. The object describes how to compile the formatted source code to 8 | the general asset type(js, css, html). 9 | 10 | key | description 11 | ------------|------------- 12 | name | Give your formatter a name for use in logging 13 | extensions | Array of source file extension excluding the dot 14 | assetType | The output type js/css/html 15 | contentType | Mime type for HTTP headers. Don't forget charset. 16 | compile | Function with the signature (pathEntry, options, cb) 17 | 18 | 19 | ### Compile function 20 | 21 | The function must read the file at `pathEntry` and render it as a string. 22 | The `cb` function must be called with the string output. 23 | 24 | You can also return an error object created with `Error()`. Throwing an error is not an option as the formatter 25 | is asynchronous. 26 | 27 | `options` provide additional information such as 28 | 29 | key | description 30 | ------------|------------- 31 | compress | True if the output should be compressed or packed for the smallest possible size. 32 | locals | Variables for use in HTML and CSS compilation. They could also be used in JavaScript expressions at pre-compile time. 33 | constants | Constants for passing to the client runtime. They could also be used in JavaScript expressions at pre-compile time. 34 | 35 | Be sure to adjust your formatter implementation based on these options. 36 | 37 | ### Logging warnings 38 | 39 | If warnings are encountered during the formatting you shouldn't output to console as it cannot be managed by Socketstream. 40 | 41 | Instead call `ss.log.warn` with the warning. Other logger calls are `ss.log.info`, `ss.log.trace` and `ss.log.error`. 42 | The default action for `ss.log.trace` is nothing. It is for debugging where it can be changed to log the trace, so feel 43 | free to call trace in your formatter. 44 | 45 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/live_reload.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Live Reloading 3 | 4 | @description 5 | # Live Reloading of Client Files 6 | 7 | Life as a front-end web developer used to be a continuous cycle of 'change file', 'reload browser', 'change file', etc etc until you got the result you were looking for. 8 | 9 | SocketStream breaks this cycle by automatically refreshing the browser whenever you make a change to any file in the `/client` directory. What's more, if you're just tweaking the stylesheets, the CSS will be reloaded in place so your app doesn't lose state. 10 | 11 | **Tip**: Open up your text editor on one side of the screen, put the browser on the other, and watch your productivity soar! 12 | 13 | 14 | Live Reload is automatically enabled unless you call: 15 |
16 |    ss.client.packAssets();
17 | 
18 | As you typically would in `production` mode. 19 | 20 | 21 | ### Known issues 22 | 23 | #### VIM 24 | 25 | VIM creates a temporary file before replacing the real file. To prevent problems with Live Reload, change the write mode with: 26 |
27 |     :set nowritebackup
28 | 
29 | 30 | #### Too many files 31 | 32 | Live Reload is built on Node's `fs.watch()` API which works differently on each operating system. For example, on Linux you'll get an `EMFILE` error if you have many files in your `client` directory. Change this limit with: 33 |
34 |     sudo vi /etc/sysctl.conf
35 | 
36 | add the following line 37 |
38 |     fs.inotify.max_user_instances = 200 # or higher if needed
39 | 
40 | then run 41 |
42 |     sudo sysctl -p
43 | 
44 | If things still don't work as expected, please log an issue and be sure to mention which OS you're using. 45 | 46 | 47 | ### Options 48 | 49 | To disable Live Reload, even in development mode, put the following in your `app.js` code: 50 |
51 |     ss.client.set({liveReload: false})
52 | 
53 | Alternatively, you may specify the top-level directories within `/client` you wish Live Reload to observe. E.g.: 54 |
55 |     ss.client.set({liveReload: ['views', 'css'])
56 | 
-------------------------------------------------------------------------------- /lib/request/index.js: -------------------------------------------------------------------------------- 1 | // Request Responders 2 | // ------------------ 3 | // Processes incoming requests regardless of transport (e.g. websocket, HTTP, method call from console) 4 | // Each responder can expose a number of interfaces - e.g. Websocket, Console, and will only respond to incoming 5 | // messages of it's type (e.g. 'rpc', 'events', etc) 6 | // Responders can optionally choose to use the middleware stack provided 7 | // The 'events' and 'rpc' responders are loaded by default, though even this can be overruled by calling clear() 8 | 'use strict'; 9 | 10 | module.exports = function(ss) { 11 | var middleware = require('./middleware')(ss), 12 | responderCount = 0, 13 | responders = {}, 14 | useDefaults = true; 15 | 16 | return { 17 | clear: function() { 18 | //jshint -W093 19 | return (useDefaults = false); 20 | }, 21 | add: function(nameOrModule, config) { 22 | config = config || null; 23 | var mod = ss.require(nameOrModule, 'request/responders',function(err) { 24 | throw new Error('Unable to find the \''+err.id+'\' Request Responder internally'); 25 | }); 26 | 27 | try { 28 | var id = nameOrModule === 'events' && '0' || ++responderCount; 29 | //jshint -W093 30 | return (responders[id] = mod(id, config, ss)); 31 | } catch (e) { 32 | var responderName = responders[id] && responders[id].name || '', 33 | err = new Error('Unable to initialize Request Responder \'' + responderName + '\''); 34 | err.stack = e.stack; 35 | throw e; 36 | } 37 | }, 38 | load: function() { 39 | var middlewareStack = middleware.load(); 40 | if (useDefaults) { 41 | this.add('events'); 42 | this.add('rpc'); 43 | } 44 | var output = {}; 45 | for (var id in responders) { 46 | var responder = responders[id]; 47 | output[id] = { 48 | name: responder.name, 49 | interfaces: responder.interfaces(middlewareStack) 50 | }; 51 | } 52 | return output; 53 | } 54 | }; 55 | }; 56 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/client_side_constants.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Client-Side Constants 3 | 4 | @description 5 | # Client-Side Constants 6 | 7 | When you load the view in the browser, constants will be defined as global variables, so they can be 8 | accessed anywhere by their name. 9 | 10 | To define a constant add it under `consts:` in the view definition. 11 | 12 | ss.client.define('myview',{ 13 | view: 'myview.html', 14 | consts: { 15 | appConfig: { 16 | facebookAppId: '1234', 17 | twitterKey: 'AAA' 18 | } 19 | } 20 | }); 21 | 22 | The constants will only be loaded in the view. Other views will not be affected. 23 | 24 | You can also define a global constant using `ss.client.send`. In this case the constant will be loaded 25 | in all views. In case you have a constant in a view definition with the same name, it will be 26 | used rather than the global value. 27 | 28 | ## Locals 29 | 30 | When you write HTML and CSS you often want to use a common value which isn't defined in the source code, 31 | but rather in configuration. Version number and copyright are good examples of these, so are color values, 32 | debugging environment and level of browser support. 33 | 34 | Following convention these are called locals. They are not available as variables in the browser. That is unless 35 | your formatter expose them as such. They are however passed to the template_engines and formatters. 36 | 37 | To define a local add it under `locals:` in the view defintion. 38 | 39 | ss.client.define('myview',{ 40 | view: 'myview.html', 41 | locals: { 42 | appConfig: { 43 | copyright: '1999' 44 | } 45 | } 46 | }); 47 | 48 | The locals will only be passed when rendering that view. Other views will not be affected. 49 | 50 | You can also define a global local(yes, naming sucks) using `ss.client.send`. In this case the local will be 51 | used in all views. In case you have a local in a view definition with the same name, it will be 52 | used rather than the global value. 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/loading_assets_on_demand.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Loading Assets On Demand 3 | 4 | @description 5 | # Loading Assets On Demand 6 | 7 | If you're writing a small app you can safely ignore this section, as it's always better to pack all client assets into one file and send everything through together in one go if possible. 8 | 9 | But what if you're writing a large app, or an app with multiple distinct sections like iCloud.com? 10 | 11 | SocketStream allows you to load code (and other assets in the future) into your app asynchronously on demand. 12 | 13 | 14 | ### Loading Code 15 | 16 | Sadly it's not possible to directly `require()` modules which haven't been loaded as the blocking nature of the `require` function means the browser would freeze until the module has been retrieved from the server - not good. 17 | 18 | However SocketStream allows you to load additional code modules from the server asynchronously using the built-in `ss.load.code()` command. Once all the code you've requested has been loaded, we execute a callback, allowing you to `require()` the new modules as normal without any fancy syntax. 19 | 20 | To try this out, create a new directory of application modules in `/client/code`. For the sake of this example, let's call our new directory `/client/code/mail`. We'll also assume this directory has a module in it called `search.js`. 21 | 22 |
23 | // in any client-side module
24 | ss.load.code('/mail', function(){
25 | 
26 |   // all modules in /client/code/mail have now been loaded into
27 |   // the root namespace (/) and can be required in the normal way
28 |   var search = require('/search');
29 | 
30 | });
31 | 
32 | 33 | Note: Regardless of the directory you load, the modules inside will always be loaded into the root (/) namespace by default. If you want to mount the new modules in a different namespace, just create one or more sub-directories in the folder you're loading. 34 | 35 | 36 | ### Automatic Caching 37 | 38 | Modules are only ever retrieved from the server once. Subsequent requests for the same directory will be returned instantly without contacting the server. -------------------------------------------------------------------------------- /test/fixtures/project/node_modules/object-assign/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "object-assign", 3 | "version": "4.0.1", 4 | "description": "ES6 Object.assign() ponyfill", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/sindresorhus/object-assign.git" 9 | }, 10 | "author": { 11 | "name": "Sindre Sorhus", 12 | "email": "sindresorhus@gmail.com", 13 | "url": "http://sindresorhus.com" 14 | }, 15 | "engines": { 16 | "node": ">=0.10.0" 17 | }, 18 | "scripts": { 19 | "test": "xo && mocha", 20 | "bench": "matcha bench.js" 21 | }, 22 | "files": [ 23 | "index.js" 24 | ], 25 | "keywords": [ 26 | "object", 27 | "assign", 28 | "extend", 29 | "properties", 30 | "es6", 31 | "ecmascript", 32 | "harmony", 33 | "ponyfill", 34 | "prollyfill", 35 | "polyfill", 36 | "shim", 37 | "browser" 38 | ], 39 | "devDependencies": { 40 | "lodash": "^3.10.1", 41 | "matcha": "^0.6.0", 42 | "mocha": "*", 43 | "xo": "*" 44 | }, 45 | "xo": { 46 | "envs": [ 47 | "node", 48 | "mocha" 49 | ] 50 | }, 51 | "gitHead": "b0c40d37cbc43e89ad3326a9bad4c6b3133ba6d3", 52 | "bugs": { 53 | "url": "https://github.com/sindresorhus/object-assign/issues" 54 | }, 55 | "homepage": "https://github.com/sindresorhus/object-assign#readme", 56 | "_id": "object-assign@4.0.1", 57 | "_shasum": "99504456c3598b5cad4fc59c26e8a9bb107fe0bd", 58 | "_from": "object-assign@*", 59 | "_npmVersion": "2.13.3", 60 | "_nodeVersion": "3.0.0", 61 | "_npmUser": { 62 | "name": "sindresorhus", 63 | "email": "sindresorhus@gmail.com" 64 | }, 65 | "dist": { 66 | "shasum": "99504456c3598b5cad4fc59c26e8a9bb107fe0bd", 67 | "tarball": "http://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz" 68 | }, 69 | "maintainers": [ 70 | { 71 | "name": "sindresorhus", 72 | "email": "sindresorhus@gmail.com" 73 | } 74 | ], 75 | "directories": {}, 76 | "_resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz", 77 | "readme": "ERROR: No README data found!" 78 | } 79 | -------------------------------------------------------------------------------- /docs/partials/tutorials/choosing_protocol.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

Choosing a Protocol

6 |

The state of web protocols is such that WebSockets are supported on perhaps 90% of the devices that reach your 7 | website. The first question you have to ask yourself is what to do about the last 10%. You can ignore them and 8 | configure SocketStream to only use WebSockets for streaming connections. This is an option that will be added 9 | in version 0.7 and be stable in 0.8.

10 |

Otherwise you can support 100% of devices with fallback protocols such as "engine.io" or "SockJS". They are both 11 | mature but with different approaches. SockJS tries to open a WebSocket and fall backs to various polling techniques 12 | if it cannot. Engine.io starts with polling and tries to upgrade ultimately to a WebSocket connection. So for 13 | 90% of devices SockJS will connect faster.

14 |

However engine.io supports compression and binary data, so you may push more data to the client. This may not 15 | be relevant for your application, so both are good options.

16 |

For streaming connections it is also important to consider Internet proxies and gateways. Many firewalls only let 17 | through HTTP and stops all other protocols. To support this you cannot use straight WebSockets as the fallback 18 | mechanisms must always be used.

19 |

Over the next year HTTP version 2 will become supported in most web browsers and web servers. It allows some 20 | advanced data caching techniques that can be used to achieve Real-Time data streaming to clients over HTTP.

21 |

Hopefully we will be able to extend SocketStream with the ability to stream over HTTP/2.

22 |

Another interesting candidate is WebRTC which allows browsers to talk to each other. This might also be fitted 23 | into the Real-Time puzzle.

24 |
25 | -------------------------------------------------------------------------------- /test/unit/client/formatters/javascript.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'), 4 | ss = require( '../../../../lib/socketstream'), 5 | fixtures = require('../../../fixtures'); 6 | 7 | 8 | describe('js formatter', function () { 9 | 10 | beforeEach(function() { 11 | 12 | // back to initial client state 13 | ss.client.reset(); 14 | ss.client.load(); 15 | }); 16 | 17 | describe('#init', function () { 18 | 19 | it('should return an object describing what file extensions the formatter handles', function() { 20 | 21 | var formatters = ss.api.client.formatters; 22 | 23 | formatters.js.should.be.type('object'); 24 | formatters.js.extensions.should.eql(['js']); 25 | }); 26 | 27 | it('should return an object describing what asset and content types that the formatter handles',function() { 28 | 29 | var formatters = ss.api.client.formatters; 30 | 31 | formatters.js.should.be.type('object'); 32 | formatters.js.assetType.should.equal('js'); 33 | formatters.js.contentType.should.equal('text/javascript; charset=utf-8'); 34 | }); 35 | 36 | }); 37 | 38 | describe('#compile', function () { 39 | 40 | it('should return the JS file content as is', function() { 41 | 42 | var concrete = ss.api.client.formatters.js; 43 | var output; 44 | concrete.call(path.join(fixtures.project, 'client/abc/index.js'),{},function(out) { 45 | output = out; 46 | out.should.be.equal('// test\n'); 47 | },function(err) { 48 | should(err).be.equal(undefined); 49 | }); 50 | }); 51 | 52 | it('should return the MAP file content as is for processing',function() { 53 | 54 | ss.client.formatters.add('map'); 55 | 56 | ss.api.bundler.load(); 57 | ss.api.client.formatters = ss.client.formatters.load(); 58 | 59 | var concrete = ss.api.client.formatters.map; 60 | var output; 61 | concrete.call(path.join(fixtures.project, 'client/abc/index.js'), {}, function (out) { 62 | output = out; 63 | out.should.be.equal('// test\n'); 64 | }, function (err) { 65 | should(err).be.equal(undefined); 66 | }); 67 | }); 68 | }); 69 | 70 | }); 71 | -------------------------------------------------------------------------------- /src/docs/tutorials/en/web_workers.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Web Workers 3 | 4 | @description 5 | # Web Workers 6 | 7 | Web Workers provide a great way for modern browsers to execute complex tasks and calculations without blocking the main thread. 8 | 9 | SocketStream makes it easy to work with Web Workers by providing transparent compilation (if you're using CoffeeScript) and automatic minification & caching when you call `ss.client.packAssets()` 10 | 11 | To read more about Web Workers in general, take a look at: http://www.html5rocks.com/en/tutorials/workers/basics 12 | 13 | 14 | ### Getting started 15 | 16 | Web Workers live in `/client/workers`. We don't create this folder by default, so you'll need to do that first. 17 | 18 | Each worker should be written as a separate `.js` or `.coffee` file (if you have the `ss-coffee` module installed). For this tutorial we'll be using JavaScript. 19 | 20 | 21 | ### Basic Example 22 | 23 | Let's create a worker which will calculate Pi using the [Leibniz Formula](http://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80): 24 | 25 | 26 |
27 | // in /client/workers/pi.js
28 | 
29 | self.addEventListener('message', function(e) {
30 |   var cycles = e.data;
31 |   postMessage("Calculating Pi using " + cycles + " cycles");
32 |   var numbers = calculatePi(cycles);
33 |   postMessage("Result: " + numbers);
34 | }, false);
35 | 
36 | function calculatePi(cycles) {
37 |   var pi = 0;
38 |   var n  = 1;
39 |   for (var i=0; i <= cycles; i++) {
40 |     pi = pi + (4/n) - (4 / (n+2));
41 |     n  = n  + 4;
42 |   }
43 |   return pi;
44 | }
45 | 
46 | 47 | Then, in any client-side code file, invoke the worker. 48 | 49 |
50 | // in any /client/code file
51 | var worker = ss.load.worker('/pi.js');
52 | 
53 | // print output to console
54 | worker.addEventListener('message', function(e) {
55 |   console.log(e.data);
56 | });
57 | 
58 | // start worker with 10000000 cycles
59 | worker.postMessage(10000000);
60 | 
61 | 62 | A few seconds after the task has run you should see the output in the browser's console: 63 | 64 | Calculating Pi using 10000000 cycles 65 | Result: 3.1415926485894077 66 | 67 | Experiment with running more cycles and passing different messages. 68 | -------------------------------------------------------------------------------- /src/docs/tutorials/index.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name index 3 | @description Documentation 4 | 5 | ##### Developing (Client-side) 6 | 7 | * [Client-side Code](http://socketstream.github.io/socketstream/docs/#/tutorials/client_side_code) 8 | * [Client-side Templates](http://socketstream.github.io/socketstream/docs/#/tutorials/client_side_templates) 9 | * [Defining multiple Single-Page Clients](http://socketstream.github.io/socketstream/docs/#/tutorials/defining_multiple_clients) 10 | * [Loading Assets On Demand](http://socketstream.github.io/socketstream/docs/#/tutorials/loading_assets_on_demand) 11 | * [Live Reload](http://socketstream.github.io/socketstream/docs/#/tutorials/live_reload) 12 | * [Web Workers](http://socketstream.github.io/socketstream/docs/#/tutorials/web_workers) 13 | 14 | ##### Developing (Server-side) 15 | 16 | * [RPC Responder](http://socketstream.github.io/socketstream/docs/#/tutorials/rpc_responder) 17 | * [Pub/Sub Events](http://socketstream.github.io/socketstream/docs/#/tutorials/pub_sub_events) 18 | * [Sessions](http://socketstream.github.io/socketstream/docs/#/tutorials/sessions) 19 | * [Request Middleware](http://socketstream.github.io/socketstream/docs/#/tutorials/request_middleware) 20 | * [HTTP Middleware](http://socketstream.github.io/socketstream/docs/#/tutorials/http_middleware) 21 | * [Authentication](http://socketstream.github.io/socketstream/docs/#/tutorials/authentication) 22 | * [Testing Your App](http://socketstream.github.io/socketstream/docs/#/tutorials/server_side_testing) 23 | 24 | ##### Best Practices 25 | 26 | * [Hosting in Production](http://socketstream.github.io/socketstream/docs/#/tutorials/production_hosting) - Packing assets, CDNs, handling exceptions 27 | 28 | ##### Extending SocketStream 29 | 30 | * [Modules](http://socketstream.github.io/socketstream/docs/#/tutorials/modules) - extending indirectly with node modules from the app. 31 | * [Writing Template Engine Wrappers](http://socketstream.github.io/socketstream/docs/#/tutorials/template_engine_wrappers) - support any of the gazillion template formats out there 32 | * [Writing Request Responders](http://socketstream.github.io/socketstream/docs/#/tutorials/writing_request_responders) - experiment with models and low-level message protocols -------------------------------------------------------------------------------- /test/unit/client/asset.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | 5 | describe('client asset handler', function () { 6 | 7 | 8 | 9 | describe('#js', function () { 10 | 11 | 12 | 13 | it('should return the compressed file, if the compress option is true'); 14 | 15 | 16 | 17 | it('should otherwise return the file'); 18 | 19 | 20 | 21 | it('should not modify the code, if it\'s in the libs directory'); 22 | 23 | 24 | 25 | it('should not add a leading slash to the path, if the file is a system module'); 26 | 27 | 28 | 29 | it('should otherwise wrap the file in a module wrapper'); 30 | 31 | 32 | 33 | it('should raise an error if the file is not found at the path'); 34 | 35 | 36 | 37 | it('should raise an error if the file is not supported'); 38 | 39 | 40 | 41 | it('should raise an error if the file is not the right type'); 42 | 43 | 44 | 45 | }); 46 | 47 | 48 | 49 | describe('#worker', function () { 50 | 51 | 52 | 53 | it('should return the compressed file, if the compress option is true'); 54 | 55 | 56 | 57 | it('should otherwise return the file'); 58 | 59 | 60 | 61 | it('should raise an error if the file is not found at the path'); 62 | 63 | 64 | 65 | it('should raise an error if the file is not supported'); 66 | 67 | 68 | 69 | it('should raise an error if the file is not the right type'); 70 | 71 | 72 | 73 | }); 74 | 75 | 76 | 77 | describe('#css', function () { 78 | 79 | 80 | 81 | it('should raise an error if the file is not found at the path'); 82 | 83 | 84 | 85 | it('should raise an error if the file is not supported'); 86 | 87 | 88 | 89 | it('should raise an error if the file is not the right type'); 90 | 91 | 92 | 93 | }); 94 | 95 | 96 | 97 | describe('#html', function () { 98 | 99 | 100 | 101 | it('should raise an error if the file is not found at the path'); 102 | 103 | 104 | 105 | it('should raise an error if the file is not supported'); 106 | 107 | 108 | 109 | it('should raise an error if the file is not the right type'); 110 | 111 | 112 | 113 | }); 114 | 115 | 116 | 117 | }); -------------------------------------------------------------------------------- /lib/session/store.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Connect - session - Store 3 | * Copyright(c) 2010 Sencha Inc. 4 | * Copyright(c) 2011 TJ Holowaychuk 5 | * MIT Licensed 6 | */ 7 | 'use strict'; 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | var EventEmitter = require('eventemitter2').EventEmitter2, 14 | debug = require('debug')('sessionstore'), 15 | Session = require('./session'), 16 | Cookie = require('./cookie'); 17 | 18 | /** 19 | * Initialize abstract `Store`. 20 | * 21 | * @param options {Object} Store options 22 | * @api private 23 | */ 24 | 25 | var Store = module.exports = function Store(){}; 26 | 27 | /** 28 | * Inherit from `EventEmitter.prototype`. 29 | */ 30 | 31 | Store.prototype = Object.create(EventEmitter.prototype); 32 | 33 | /** 34 | * Re-generate the given requests's session. 35 | * 36 | * @param {IncomingRequest} req 37 | * @return {Function} fn 38 | * @api public 39 | */ 40 | 41 | Store.prototype.regenerate = function(req, fn){ 42 | var self = this; 43 | this.destroy(req.sessionID, function(err){ 44 | self.generate(req); 45 | fn(err); 46 | }); 47 | }; 48 | 49 | /** 50 | * Load a `Session` instance via the given `sid` 51 | * and invoke the callback `fn(err, sess)`. 52 | * 53 | * @param {String} sid 54 | * @param {Function} fn 55 | * @api public 56 | */ 57 | 58 | Store.prototype.load = function(sid, fn){ 59 | var self = this; 60 | this.get(sid, function(err, sess){ 61 | if (err) { debug('failed to get %s',sid); return fn(err); } 62 | if (!sess) { debug('no session for %s',sid); return fn(); } 63 | var req = { sessionID: sid, sessionStore: self }; 64 | sess = self.createSession(req, sess); // recreate prototypes on persisted data 65 | fn(null, sess); 66 | }); 67 | }; 68 | 69 | /** 70 | * Create session from JSON `sess` data. 71 | * 72 | * @param {IncomingRequest} req 73 | * @param {Object} sess 74 | * @return {Session} 75 | * @api private 76 | */ 77 | 78 | Store.prototype.createSession = function(req, sess){ 79 | var expires = sess.cookie.expires 80 | , orig = sess.cookie.originalMaxAge; 81 | sess.cookie = new Cookie(sess.cookie); 82 | if ('string' === typeof expires) { sess.cookie.expires = new Date(expires); } 83 | sess.cookie.originalMaxAge = orig; 84 | req.session = new Session(req, sess); 85 | return req.session; 86 | }; 87 | -------------------------------------------------------------------------------- /docs/partials/api/ss.client.client.html: -------------------------------------------------------------------------------- 1 | Improve this doc View source

client 2 |
service in module ss 3 | 4 |
5 |

6 |

Description

7 |

Allow other libs to send assets to the client

8 |
9 |

Methods

10 |
  • send(type, name, content, options)

    11 |

    Allow other libs to send assets to the client. add new System Library or Module

    12 |
    Parameters
    ParamTypeDetails
    type'code','lib','module'
      13 |
    • code, lib, module.
    • 14 |
    15 |
    namestring
      16 |
    • Module name for require.
    • 17 |
    18 |
    contentstring
      19 |
    • The JS code
    • 20 |
    21 |
    optionsObject
      22 |
    • Allows you to specify compress and coffee format flags.
    • 23 |
    24 |
    25 |
  • 26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /test/unit/utils/unique_set.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'), 4 | UniqueSet = require( path.join('../../..', 'lib/utils/unique_set') ).UniqueSet, 5 | set; 6 | 7 | describe('lib/utils/unique_set', function () { 8 | 9 | beforeEach(function (done) { 10 | 11 | 12 | set = new UniqueSet; 13 | done(); 14 | }); 15 | 16 | it('should add elements', function (done) { 17 | 18 | set.add('tom', 12345); 19 | set.add('tom', 12345); 20 | set.members('tom').toString().should.equal('12345'); 21 | 22 | set.add('tom', null); 23 | set.members('tom').toString().should.equal('12345'); 24 | done(); 25 | }); 26 | 27 | it('should remove an element', function (done) { 28 | 29 | set.add('tom', 1111); 30 | set.add('tom', 1112); 31 | set.add('john', 2222); 32 | set.members('tom').join(',').should.equal('1111,1112'); 33 | 34 | set.remove('tom', 1111); 35 | set.members('tom').join(',').should.equal('1112'); 36 | 37 | set.remove('tom', 1112); 38 | set.members('tom').should.be.type('object'); 39 | set.members('tom').length.should.equal(0); 40 | 41 | done(); 42 | }); 43 | 44 | it('should list keys in a set', function (done) { 45 | 46 | set.add('tom', 1111); 47 | set.add('tom', 1112); 48 | set.add('john', 1113); 49 | set.add('paul', 1114); 50 | set.add(null, 1115); 51 | 52 | set.keys().join(',').should.equal('tom,john,paul'); 53 | 54 | done(); 55 | }); 56 | 57 | it('should remove a value across all keys', function (done) { 58 | 59 | set.add('channel1', 1111); 60 | set.add('channel1', 1112); 61 | set.add('channel2', 1112); 62 | set.add('channel3', 1111); 63 | set.add('channel4', 1114); 64 | set.add('channel5', 1111); 65 | set.members('channel1').join(',').should.equal('1111,1112'); 66 | set.members('channel3').join(',').should.equal('1111'); 67 | set.members('channel5').join(',').should.equal('1111'); 68 | 69 | set.removeFromAll(1111); 70 | set.members('channel1').join(',').should.equal('1112'); 71 | set.members('channel3').join(',').should.equal(''); 72 | set.members('channel5').join(',').should.equal(''); 73 | 74 | done(); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /lib/client/bundler/production.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //TODO just load from assets folder 4 | 5 | module.exports = function(ss,client,options){ 6 | 7 | var bundler = ss.bundler.create({ 8 | define: define, 9 | asset: asset 10 | }); 11 | return bundler; 12 | 13 | 14 | function define(paths) { 15 | 16 | if (typeof paths.view !== 'string') { 17 | throw new Error('You may only define one HTML view per single-page client. Please pass a filename as a string, not an Array'); 18 | } 19 | if (paths.view.lastIndexOf('.') <= 0) { 20 | throw new Error('The \'' + paths.view + '\' view must have a valid HTML extension (such as .html or .jade)'); 21 | } 22 | 23 | // Define new client object 24 | client.paths = ss.bundler.sourcePaths(paths); 25 | client.constants = paths.constants || paths.consts; 26 | client.locals = paths.locals; 27 | client.entryInitPath = ss.bundler.findEntryPoint(client); 28 | } 29 | 30 | //TODO callback(err,output) for pack to flag error 31 | function asset(entry, opts, cb) { 32 | ss.bundler.loadFile(entry, opts, null, 33 | function(output) { 34 | switch(entry.bundle) { 35 | case 'html': 36 | return cb(ss.bundler.injectTailIfNeeded(output,opts)); 37 | case 'css': 38 | return cb( client.includes.css? output:''); 39 | case 'worker': 40 | //TODO 41 | if (opts.compress && entry.file.indexOf('.min') === -1) { 42 | output = ss.bundler.minifyJSFile(output, entry.file); 43 | } 44 | break; 45 | 46 | default: 47 | //TODO with options compress saved to avoid double compression 48 | output = bundler.wrapCode(output, entry, opts); 49 | if (opts.compress && entry.file.indexOf('.min') === -1) { 50 | output = ss.bundler.minifyJSFile(output, entry.file); 51 | } 52 | return cb(output); 53 | } 54 | }, 55 | function(err) { 56 | ss.log.clientIssue(client,options,err,entry); 57 | switch(entry.ext) { 58 | case 'html': 59 | return cb('Couldn\'t format ' + entry.file + err.userInfoHTML); 60 | case 'css': 61 | return cb('/* couldn\'t format ' + entry.file + err.userInfoText+' */'); 62 | default: 63 | return cb('// couldn\'t format ' + entry.file + err.userInfoText); 64 | } 65 | }); 66 | } 67 | }; 68 | 69 | -------------------------------------------------------------------------------- /docs/partials/tutorials/client_side_constants.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

Client-Side Constants

6 |

When you load the view in the browser, constants will be defined as global variables, so they can be 7 | accessed anywhere by their name.

8 |

To define a constant add it under consts: in the view definition.

9 |
ss.client.define('myview',{
10 |     view: 'myview.html',
11 |     consts: {
12 |       appConfig: {
13 |         facebookAppId: '1234',
14 |         twitterKey: 'AAA'
15 |       }
16 |     }
17 | });
18 | 

The constants will only be loaded in the view. Other views will not be affected.

19 |

You can also define a global constant using ss.client.send. In this case the constant will be loaded 20 | in all views. In case you have a constant in a view definition with the same name, it will be 21 | used rather than the global value.

22 |

Locals

23 |

When you write HTML and CSS you often want to use a common value which isn't defined in the source code, 24 | but rather in configuration. Version number and copyright are good examples of these, so are color values, 25 | debugging environment and level of browser support.

26 |

Following convention these are called locals. They are not available as variables in the browser. That is unless 27 | your formatter expose them as such. They are however passed to the template_engines and formatters.

28 |

To define a local add it under locals: in the view defintion.

29 |
ss.client.define('myview',{
30 |     view: 'myview.html',
31 |     locals: {
32 |       appConfig: {
33 |         copyright: '1999'
34 |       }
35 |     }
36 | });
37 | 

The locals will only be passed when rendering that view. Other views will not be affected.

38 |

You can also define a global local(yes, naming sucks) using ss.client.send. In this case the local will be 39 | used in all views. In case you have a local in a view definition with the same name, it will be 40 | used rather than the global value.

41 |
42 | -------------------------------------------------------------------------------- /src/docs/tutorials/ko/live_reload.md: -------------------------------------------------------------------------------- 1 | # 클라이언트 파일의 라이브 리로딩 2 | 3 | 4 | 웹개발자의 일상은 보통 코드를 바꾸고, 브라우져를 리로드하고, 코드를 바꾸고의 연속입니다. 원하는 결과가 나올때 까지는요. 5 | 6 | 7 | 소켓스트림은 `client`디랙토리의 파일이 수정되면 자동으로 브라우저를 리로드해서 이 굴래를 깨버렸습니다. 8 | 9 | 10 | 이 기능은 특히 CSS나 HTML을 만질때 유용합니다. 그냥 모니터 한편에 텍스트 에디터를 여시고 브라우저를 반대편에 두시고 생산성이 치솓는걸 느끼세요. 11 | 12 | 13 | 라이브 리로드는 다음 명령이 불리지 않는 이상 자동으로 실행됩니다: 14 | 15 | 16 | ss.client.packAssets() 17 | 18 | 보통 이건 `production` 모드에서 하죠? 19 | 20 | 21 | 22 | ### 알려진 문제점 23 | 24 | 25 | 라이브 리로드는 노드의 `fs.watch()`API 에 포함되어있습니다. 이는 운영체재마다 다르게 작동합니다. 예를들어 리눅스에선 `client` 디랙토리에 파일이 많을 경우 `EMFILE`에러가 나옵니다. 이런 경우 다음 파일을 여시고: 26 | 27 | 28 | sudo vi /etc/sysctl.conf 29 | 30 | 다음 라인을 추가합니다. 31 | 32 | 33 | fs.inotify.max_user_instances = 200 # 필요하면 더 올리셔도 됩니다. 34 | 35 | 36 | 그리고 다음 명령을 실행 37 | 38 | 39 | sudo sysctl -p 40 | 41 | 만약 여전히 원하는 대로 안되면 사용하는 OS와 에러로그로 이슈를 등록해주세요. 42 | 43 | 44 | 45 | ### 옵션 46 | 47 | 48 | 개발환경이라도 라이브 리로드를 끄고 싶으시면, 다음코드를 `app.js` 에 넣으세요. 49 | 50 | 51 | ss.client.set({liveReload: false}) 52 | 53 | `/client`내에서 라이브 리로드를 할 최상위 디랙토리를 지정하고 싶으시다면 이렇게 하세요. 54 | 55 | ss.client.set({liveReload: ['views', 'css']) 56 | -------------------------------------------------------------------------------- /lib/client/formatters.js: -------------------------------------------------------------------------------- 1 | // Code Formatters 2 | // --------------- 3 | // Loads default code formatters and presents an API for loading custom formatters 4 | 'use strict'; 5 | var debug = require('debug')('socketstream:client'); 6 | 7 | /** 8 | * @ngdoc service 9 | * @name client.formatters:formatters 10 | * @description 11 | * Formatter registry 12 | */ 13 | module.exports = function(ss,options) { 14 | var mods = []; 15 | return { 16 | /** 17 | * @ngdoc method 18 | * @name client.formatters:formatters#add 19 | * @methodOf client.formatters:formatters 20 | * @function 21 | * Define a formatter for client asset (JS/CSS/HTML) rendering 22 | * @param {string|object} nameOrModule the formatter object or name 23 | * @param {object} config parameters configuring the formatter 24 | */ 25 | add: function(nameOrModule, config) { 26 | var formatter; 27 | config = config || {}; 28 | switch(typeof nameOrModule) { 29 | case 'object': 30 | formatter = nameOrModule.init(ss.root, config, options); 31 | addCall(formatter); 32 | break; 33 | 34 | case 'function': 35 | case 'string': 36 | var mod = ss.require(nameOrModule, 'client/formatters',function(err) { 37 | throw new Error('The "'+err.id+'" formatter is not supported by '+ 38 | 'SocketStream internally. Please pass a compatible module instead'); 39 | }); 40 | formatter = mod(ss, config, options); 41 | addCall(formatter,true); 42 | break; 43 | } 44 | 45 | return mods.push(formatter); 46 | }, 47 | load: function() { 48 | var byExtension = {}; 49 | mods.forEach(function (mod) { 50 | return mod.extensions.forEach(function (extension) { 51 | byExtension[extension] = mod; 52 | }); 53 | }); 54 | debug('Formatters supported by extension: %s', Object.keys(byExtension).join(',')); 55 | return byExtension; 56 | }, 57 | forget: function() { 58 | mods.length = 0; 59 | } 60 | }; 61 | 62 | function addCall(formatter) { 63 | formatter.call = function(path,options,cb,errCb) { 64 | try{ 65 | formatter.compile(path,options,function(result) { 66 | if (typeof result === 'string') { return cb(result); } 67 | else { return errCb(result); } 68 | },errCb); 69 | } 70 | catch(ex) { 71 | return errCb(ex); 72 | } 73 | }; 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /test/unit/tasks/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ss = require( '../../../lib/socketstream'), 4 | //options = ss.client.options, 5 | //defineAbcClient = require('./abcClient'), 6 | fixtures = require('../../fixtures'); 7 | 8 | 9 | describe('start tasks plan', function () { 10 | var should = require('should'); 11 | 12 | fixtures.setProject('project'); 13 | 14 | beforeEach(function () { 15 | }); 16 | 17 | function start() { 18 | return ss.tasks.plan(arguments); 19 | } 20 | 21 | it('should plan with standard start call', function() { 22 | var server = require('http').createServer(); 23 | 24 | // ss.start(server) 25 | var plan = start(server); 26 | 27 | plan.httpServer.should.equal(server); 28 | plan.targets.should.eql(['default']); 29 | }); 30 | 31 | it('should plan with start() call', function() { 32 | // ss.start() 33 | var plan = start(); 34 | 35 | should(plan.httpServer).be.equal(null); 36 | plan.targets.should.eql(['default']); 37 | }); 38 | 39 | it('should plan with start("pack-all") call', function() { 40 | 41 | // ss.start("pack-all") 42 | var plan = start('pack-all'); 43 | 44 | should(plan.httpServer).be.equal(null); 45 | plan.targets.should.eql(['pack-all']); 46 | }); 47 | 48 | it('should plan with start(server,"pack-all") call', function() { 49 | var server = require('http').createServer(); 50 | 51 | // ss.start(server, "pack-all") 52 | var plan = start(server, 'pack-all'); 53 | 54 | should(plan.httpServer).be.equal(server); 55 | plan.targets.should.eql(['pack-all']); 56 | }); 57 | 58 | it('should plan with start("pack-all","more-stuff") call', function() { 59 | // ss.start("pack-all","more-stuff") 60 | var plan = start('pack-all','more-stuff'); 61 | 62 | should(plan.httpServer).be.equal(null); 63 | plan.targets.should.eql(['pack-all','more-stuff']); 64 | }); 65 | 66 | it('should plan with start(server, "pack-all","more-stuff") call', function() { 67 | var server = require('http').createServer(); 68 | 69 | // ss.start("pack-all","more-stuff") 70 | var plan = start(server, 'pack-all','more-stuff'); 71 | 72 | should(plan.httpServer).be.equal(server); 73 | plan.targets.should.eql(['pack-all','more-stuff']); 74 | }); 75 | 76 | it('should plan with start("pack-all",function) call', function() { 77 | var plan = start('pack-all', function() {}); 78 | 79 | should(typeof plan.callback).be.equal('function'); 80 | plan.targets.should.eql(['pack-all']); 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /lib/session/channels.js: -------------------------------------------------------------------------------- 1 | // Session Channels 2 | // ---------------- 3 | // Provides an interface allowing you to subscribe or unsubscribe the session to a private channel 4 | 'use strict'; 5 | 6 | require('colors'); 7 | 8 | // Stores the relationship between sessionId and socketIds 9 | var subscriptions = require('../websocket/subscriptions'); 10 | 11 | module.exports = function(ss, session, socketId) { 12 | return { 13 | 14 | // Lists all the channels the client is currently subscribed to 15 | list: function() { 16 | return session.channels || []; 17 | }, 18 | 19 | // Subscribes the client to one or more channels 20 | subscribe: function(names, cb) { 21 | if (!cb) { 22 | cb = function() {}; 23 | } 24 | if (!session.channels) { 25 | session.channels = []; 26 | } 27 | forceArray(names).forEach(function(name) { 28 | if (session.channels.indexOf(name) === -1) { // clients can only join a channel once 29 | session.channels.push(name); 30 | return ss.log.info('i'.green + ' subscribed sessionId '.grey + session.id + ' to channel '.grey + name); 31 | } 32 | }); 33 | this._bindToSocket(); 34 | return session.save(cb); 35 | }, 36 | 37 | // Unsubscribes the client from one or more channels 38 | unsubscribe: function(names, cb) { 39 | if (!cb) { 40 | cb = function() {}; 41 | } 42 | if (!session.channels) { 43 | session.channels = []; 44 | } 45 | forceArray(names).forEach(function(name) { 46 | var i; 47 | if ((i = session.channels.indexOf(name)) >= 0) { 48 | session.channels.splice(i, 1); 49 | subscriptions.channel.remove(name, socketId); 50 | return ss.log.info('i'.green + ' unsubscribed sessionId '.grey + session.id + ' from channel '.grey + name); 51 | } 52 | }); 53 | return session.save(cb); 54 | }, 55 | 56 | // Unsubscribes the client from all channels 57 | reset: function(cb) { 58 | if (!cb) { 59 | cb = function() {}; 60 | } 61 | return this.unsubscribe(this.list(), cb); 62 | }, 63 | _bindToSocket: function() { 64 | if (!session.channels) { 65 | session.channels = []; 66 | } 67 | return forceArray(session.channels).forEach(function(name) { 68 | return subscriptions.channel.add(name, socketId); 69 | }); 70 | } 71 | }; 72 | }; 73 | 74 | // Private 75 | 76 | function forceArray(input) { 77 | return typeof input === 'object' && input.slice() || [input]; 78 | } 79 | -------------------------------------------------------------------------------- /docs/partials/tutorials/loading_assets_on_demand.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

Loading Assets On Demand

6 |

If you're writing a small app you can safely ignore this section, as it's always better to pack all client assets into one file and send everything through together in one go if possible.

7 |

But what if you're writing a large app, or an app with multiple distinct sections like iCloud.com?

8 |

SocketStream allows you to load code (and other assets in the future) into your app asynchronously on demand.

9 |

Loading Code

10 |

Sadly it's not possible to directly require() modules which haven't been loaded as the blocking nature of the require function means the browser would freeze until the module has been retrieved from the server - not good.

11 |

However SocketStream allows you to load additional code modules from the server asynchronously using the built-in ss.load.code() command. Once all the code you've requested has been loaded, we execute a callback, allowing you to require() the new modules as normal without any fancy syntax.

12 |

To try this out, create a new directory of application modules in /client/code. For the sake of this example, let's call our new directory /client/code/mail. We'll also assume this directory has a module in it called search.js.

13 |
14 | // in any client-side module
15 | ss.load.code('/mail', function(){
16 | 
17 |   // all modules in /client/code/mail have now been loaded into
18 |   // the root namespace (/) and can be required in the normal way
19 |   var search = require('/search');
20 | 
21 | });
22 | 
23 |

Note: Regardless of the directory you load, the modules inside will always be loaded into the root (/) namespace by default. If you want to mount the new modules in a different namespace, just create one or more sub-directories in the folder you're loading.

24 |

Automatic Caching

25 |

Modules are only ever retrieved from the server once. Subsequent requests for the same directory will be returned instantly without contacting the server.

26 |
27 | -------------------------------------------------------------------------------- /test/unit/utils/require.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ss = require('../../fixtures/socketstream'), 4 | path = require('path'), 5 | fixtures = require('../../fixtures'), 6 | chai = require('chai'), 7 | expect = chai.expect, 8 | sinon = require('sinon'), 9 | sinonChai = require('sinon-chai'); 10 | chai.use(sinonChai); 11 | 12 | describe('ss.require', function() { 13 | 14 | ss.root = ss.api.root = fixtures.project; 15 | 16 | it('should load builtin default bundler', function() { 17 | var mod = ss.api.require('default','client/bundler'); 18 | expect(mod).to.equal(require('../../../lib/client/bundler/default')); 19 | }); 20 | 21 | it('should load builtin production bundler', function() { 22 | var mod = ss.api.require('production','client/bundler'); 23 | expect(mod).to.equal(require('../../../lib/client/bundler/production')); 24 | }); 25 | 26 | it('should load builtin jade formatter', function() { 27 | var mod = ss.api.require('jade','client/formatters'); 28 | expect(mod).to.equal(require('../../../lib/client/formatters/jade')); 29 | }); 30 | 31 | it('should load builtin sass formatter', function() { 32 | var mod = ss.api.require('sass','client/formatters'); 33 | expect(mod).to.equal(require('../../../lib/client/formatters/sass')); 34 | }); 35 | 36 | it('should call error callback if module not found', function() { 37 | var spy = sinon.spy(); 38 | var mod = ss.api.require('not-there','client/formatters',spy); 39 | expect(mod).to.equal(undefined); 40 | expect(spy).to.have.been.calledWith(); 41 | }); 42 | 43 | it('should call error callback if module undefined', function() { 44 | var spy = sinon.spy(); 45 | var mod = ss.api.require(undefined,'client/formatters',spy); 46 | expect(mod).to.equal(undefined); 47 | expect(spy).to.have.been.calledWith(); 48 | }); 49 | 50 | it('should call error callback if module undefined', function() { 51 | var spy = sinon.spy(); 52 | var mod = ss.api.require(null,'client/formatters',spy); 53 | expect(mod).to.equal(undefined); 54 | expect(spy).to.have.been.calledWith(); 55 | }); 56 | 57 | it('should load package from project', function() { 58 | var mod = ss.api.require('object-assign'); 59 | expect(mod).to.be.a('function'); 60 | }); 61 | 62 | it('should identify path of package from project', function() { 63 | var p = ss.api.require.resolve('object-assign'); 64 | expect(p).to.equal(path.join(__dirname, '../../fixtures','project/node_modules/object-assign/index.js')); 65 | }); 66 | 67 | it('should identify default when primary not defined'); 68 | it('should identify default when primary not found'); 69 | }); 70 | 71 | describe('ss.require.forEach', function() { 72 | it('should identify modules matching given patterns'); 73 | }); 74 | -------------------------------------------------------------------------------- /test/unit/request/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'), 4 | ss = require( '../../fixtures/socketstream' ), 5 | chai = require('chai'), 6 | expect = chai.expect; 7 | 8 | 9 | describe('request responder API', function () { 10 | 11 | beforeEach(function() { 12 | // ss.reset(); 13 | }); 14 | 15 | it('should return an object with add, load, and clear functions', function () { 16 | expect(ss.responders).to.be.an('object'); 17 | expect(ss.responders.add).to.be.an('function'); 18 | expect(ss.responders.load).to.be.an('function'); 19 | expect(ss.responders.clear).to.be.an('function'); 20 | }); 21 | 22 | it('should set default middleware to false, so that they are not loaded', function () { 23 | expect(ss.responders.clear()).to.equal(false); 24 | }); 25 | 26 | it('should load the events and rpc responders by default', function () { 27 | var responders = ss.responders.load(); 28 | expect(responders['0'].name).to.equal('events'); 29 | expect(responders['1'].name).to.equal('rpc'); 30 | expect(responders['0'].interfaces.websocket).to.be.a('function'); 31 | expect(responders['1'].interfaces.websocket).to.be.a('function'); 32 | expect(responders['1'].interfaces.internal).to.be.a('function'); 33 | }); 34 | 35 | describe('when given a name of a request responder', function () { 36 | 37 | it('should load that request responder from inside of responders folder', function () { 38 | var loaded = ss.responders.add('rpc'); 39 | expect(loaded.name).to.equal('rpc'); 40 | expect(loaded.interfaces).to.be.a('function'); 41 | }); 42 | 43 | it('should throw an error if name unknown', function() { 44 | expect(function () { 45 | ss.responders.add('webrtc'); 46 | }).to.throw(); 47 | }); 48 | }); 49 | 50 | describe('when given a function for a request responder', function () { 51 | 52 | it('should load the request responder', function () { 53 | function responderFunc() { 54 | return { 55 | name: 'customResponder', 56 | interfaces: function () {} 57 | } 58 | } 59 | ss.responders.clear(); 60 | ss.responders.add(responderFunc); 61 | var fullLoad = ss.responders.load(); 62 | expect(fullLoad['6'].name).to.equal('customResponder'); // perhaps this could be a better behaviour just re-adding 'rpc' 63 | }); 64 | 65 | it('should throw an error if constructor does', function () { 66 | function responderFunc() { 67 | throw new Error('An error occurred loading the responder'); 68 | } 69 | ss.responders.clear(); 70 | expect(function() { 71 | ss.responders.add(responderFunc); 72 | }).to.throw(); 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /lib/utils/misc.js: -------------------------------------------------------------------------------- 1 | /*jslint bitwise: true*/ 2 | 3 | 'use strict'; 4 | 5 | /** 6 | * @ngdoc service 7 | * @name utils.misc:misc 8 | * @function 9 | * 10 | * @description 11 | * Module contains misc methods 12 | */ 13 | 14 | 15 | /** 16 | * @ngdoc service 17 | * @name utils.misc#randomString 18 | * @methodOf utils.misc:misc 19 | * @function 20 | * 21 | * @description 22 | * Adapted from http://www.broofa.com/Tools/Math.uuid.js 23 | * 24 | * @param {String} len Length of the expected string 25 | * @return {String} uuid string 26 | */ 27 | exports.randomString = function (len) { 28 | var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''), 29 | uuid = [], 30 | radix = chars.length, 31 | i; 32 | 33 | for (i = 0; i < len; i++) { 34 | uuid[i] = chars[0 | Math.random()*radix]; 35 | } 36 | return uuid.join(''); 37 | }; 38 | 39 | /** 40 | * @ngdoc service 41 | * @name utils.misc#parseWsMessage 42 | * @methodOf utils.misc:misc 43 | * @function 44 | * 45 | * @description 46 | * Parse incoming websocket messages into message type and contents 47 | * 48 | * @param {String} msg Message to parse 49 | * @return {Array} Array, where [0] is message type, [1] is message's body 50 | */ 51 | exports.parseWsMessage = function (msg) { 52 | var i; 53 | if ((i = msg.indexOf('|')) > 0) { 54 | return [msg.substr(0, i), msg.substr(i+1)]; 55 | } else { 56 | throw new Error('Invalid message'); 57 | } 58 | }; 59 | 60 | /** 61 | * @ngdoc service 62 | * @name utils.misc#extend 63 | * @methodOf utils.misc:misc 64 | * @function 65 | * 66 | * @description 67 | * Taken from underscore.js 68 | * 69 | * @param {Object} obj Object to extend 70 | * @param {} to extend with 71 | * @return {Object} Extended object 72 | */ 73 | exports.extend = function (obj) { 74 | var source, 75 | prop, 76 | i; 77 | 78 | for (i = 1; i < arguments.length; i++) { 79 | source = arguments[i]; 80 | for (prop in source) { 81 | if (source.hasOwnProperty(prop)) { 82 | obj[prop] = source[prop]; 83 | } 84 | } 85 | } 86 | return obj; 87 | }; 88 | 89 | /** 90 | * @ngdoc service 91 | * @name utils.misc#defaults 92 | * @methodOf utils.misc:misc 93 | * @function 94 | * 95 | * @description 96 | * Useful for declaring default parameter 97 | * 98 | * @param {Object} args Arguments 99 | * @param {Object} defaults Default arguments 100 | * @return {Object} Extended object 101 | */ 102 | exports.defaults = function (args, defaults) { 103 | return exports.extend({}, defaults, args); 104 | }; 105 | -------------------------------------------------------------------------------- /docs/partials/tutorials/web_workers.html: -------------------------------------------------------------------------------- 1 | Improve this doc

2 |
3 |
4 |

5 |

Web Workers

6 |

Web Workers provide a great way for modern browsers to execute complex tasks and calculations without blocking the main thread.

7 |

SocketStream makes it easy to work with Web Workers by providing transparent compilation (if you're using CoffeeScript) and automatic minification & caching when you call ss.client.packAssets()

8 |

To read more about Web Workers in general, take a look at: http://www.html5rocks.com/en/tutorials/workers/basics

9 |

Getting started

10 |

Web Workers live in /client/workers. We don't create this folder by default, so you'll need to do that first.

11 |

Each worker should be written as a separate .js or .coffee file (if you have the ss-coffee module installed). For this tutorial we'll be using JavaScript.

12 |

Basic Example

13 |

Let's create a worker which will calculate Pi using the Leibniz Formula:

14 |
15 | // in /client/workers/pi.js
16 | 
17 | self.addEventListener('message', function(e) {
18 |   var cycles = e.data;
19 |   postMessage("Calculating Pi using " + cycles + " cycles");
20 |   var numbers = calculatePi(cycles);
21 |   postMessage("Result: " + numbers);
22 | }, false);
23 | 
24 | function calculatePi(cycles) {
25 |   var pi = 0;
26 |   var n  = 1;
27 |   for (var i=0; i <= cycles; i++) {
28 |     pi = pi + (4/n) - (4 / (n+2));
29 |     n  = n  + 4;
30 |   }
31 |   return pi;
32 | }
33 | 
34 |

Then, in any client-side code file, invoke the worker.

35 |
36 | // in any /client/code file
37 | var worker = ss.load.worker('/pi.js');
38 | 
39 | // print output to console
40 | worker.addEventListener('message', function(e) {
41 |   console.log(e.data);
42 | });
43 | 
44 | // start worker with 10000000 cycles
45 | worker.postMessage(10000000);
46 | 
47 |

A few seconds after the task has run you should see the output in the browser's console:

48 |
Calculating Pi using 10000000 cycles
49 | Result: 3.1415926485894077
50 | 

Experiment with running more cycles and passing different messages.

51 |
52 | --------------------------------------------------------------------------------