Contents

Introduction

StratifiedJS modernizes the JavaScript language for use in non-trivial web applications:

No more asynchronous spaghetti. Underpinned by ideas from the Orc process calculus, SJS offers all the advantages of asynchronous programming with conventional sequential syntax. Perform asynchronous requests without callbacks. Pause program execution without setTimeout. Orchestrate complex asynchronous logic with intuitive high-level operators. See the Language Reference for more details.

Structure. A modern CommonJS-style require() system operating without callbacks or boilerplate makes it easy to structure large codebases.

Building blocks included. The Standard Module Libray with its 30+ modules gives you a good foundation for your web application.

Fluent and functional in spirit. SJS's doubledot operator makes it possible to write beautiful fluent APIs (such as e.g. the sequence module) without invasive monkey-patching or adding clunky object-oriented wrappers.

Advanced syntax features. SJS includes some of the best features proposed for upcoming JavaScript standards, making for an all-around more pleasurable, powerful and safe coding experience:

Available today. SJS works in the browser or on the server (nodejs) by including a small lib (no extension/plugin/binary required). All major browsers, desktop and mobile, down to IE6 are supported. The browser lib is ~25KB gzipped. Free and open-source.

Familiar and compatible. It might be stratified, but it still is JavaScript. If you know normal JavaScript, you'll have no trouble getting up to speed. StratifiedJS also interacts just fine with legacy JavaScript code, so it's not an "all-or-nothing" option.

Watch the Screencast

We've put together a 20-minute screencast covering the basics of StratifiedJS, as well as some demos illustrating some of its unique features:

Try it right here

You can evaluate StratifiedJS code right here in this inline console:

Try typing in

require('github:/onilabs/sjs-webapi/master/google').search('stratifiedjs');

This will return an object with google search results mentioning "stratifiedjs". Behind the scenes the browser will asynchronously load the module google.sjs straight from the Github onilabs/sjs-webapi repo and then perform a request to the Google search API. No need for callbacks - with StratifiedJS you can code with asynchronous code in a conventional, sequential style.

This is only scratching the surface though. StratifiedJS provides native constructs for coordinating and composing multiple simultaneous code paths in rich and modular ways. Find out more in the Language reference.

Downloading/Running StratifiedJS

Using npm:

$ npm install -g stratifiedjs
$ sjs --help

Using bower:

$ bower install stratifiedjs
$ ./components/stratifiedjs/sjs --help

Manual install:

StratifiedJS is hosted on GitHub. You can download a zip file with the latest stable release (0.18) here:

Development checkout:

Alternatively, you can clone the github repository and check out the stable branch or the development master branch:

$ git clone git://github.com/onilabs/stratifiedjs.git
$ cd stratifiedjs

# Current stable branch:
$ git checkout 0.18
# Development master branch:
$ git checkout master

# To run the SJS repl:
$ ./sjs --help

There's no need to run any buildscript - everything is already pre-built. You only need to rebuild (by running ./src/build/make-sjs) if you make modifications in the src/ directory.

Using StratifiedJS in the browser

To use Oni StratifiedJS in the browser, load the stratified.js script in your app's HTML, and place your StratifiedJS code into <script type="text/sjs"> tags:

<html>
  <head>
    <script src="http://path/to/your/stratified.js"></script>
    <script type="text/sjs">
      // Your SJS code here!
    </script>
  </head>
</html>

The stratified.js script is located in the stratifiedjs/ directory. You can serve this file from any location on your webserver. No other files are required for client-side StratifiedJS. However, if you want to use any modules from the Standard Module Library, then you need to serve the modules/ directory from your webserver as well (relative to the location of the stratified.js script).

On document load, StratifiedJS scans the document for <script> elements with type set to "text/sjs", transforms the StratifiedJS code it finds to 'base-level' JS (this sounds heavy, but it is actually very fast!), and executes it.

If you're looking for a server that can itself be programmed in StratifiedJS, serves up SJS code pre-compiled, and has many other features for web app programming, check out Conductance.

External scripts: If you want to keep your SJS out of inline <script> tags, the browser unfortunately won't understand a src attribute which points to a .sjs module. Instead, you should set the "main" attribute on the stratifiedjs <script> tag itself. That is:

<!-- INCORRECT: -->
<script src="http://[...]/stratified.js"></script>
<script type="text/sjs" src="./init.sjs"></script>
<!-- CORRECT: -->
<script src="http://[...]/stratified.js" main="./init"></script>

This will automatically run ./init.sjs as the main module once the page loads. You can only run one module in this way — further code should be loaded using the normal require() function.

Using StratifiedJS on the server

For server-side StratifiedJS use, you need to have a recent version of nodejs installed and reachable via your $PATH.

To execute StratifiedJS files or for a Read-Eval-Print Loop, run the sjs executable (located in the stratifiedjs/ root directory):

Usage: sjs [options] [script.sjs [arguments]]

Without a script.sjs argument you get dropped into a stratified REPL.

Options:
  -h, --help        display this help message
  -e, --eval STR    evaluate STR

When running on the command line like this, sjs code runs on top of nodejs — so the nodejs global objects are available, in addition to the StratifiedJS module system.

Alternatively, check out the StratifiedJS web app server Conductance.

The Module System

Oni StratifiedJS implements a CommonJS-like module system, which allows you to load StratifiedJS code modules in the same way on both the server and the browser.

A module is a file with extension *.sjs containing StratifiedJS code which, on loading, will be evaluated in its own scope. Variables and functions defined within the module will not be seen by other modules or by top-level code unless explicitly exported.

You export symbols from a module by adding them to the variable exports:

// mymodule.sjs
var B = 123;
function add(a,b) { return a+b; }
exports.A = 456;
exports.calc = function(a,b) { alert(add(a,b)); };

In this module, B and add would be hidden from the outside world, whereas, A and calc would be visible.

To load a module, use the function require(), passing in a module identifier:

  // load mymodule.sjs from same directory as caller:
  var mymodule = require("./mymodule.sjs"); 

  // 'sjs' extension is optional:
  var mymodule = require("./mymodule");

  // load from absolute HTTP URL:
  var mymodule = require("http://my.server.com/mymodule");

  // load module foo directly from GitHub
  // (https://github.com/afri/testmodules, branch master):
  require("github:/afri/testmodules/master/foo");

  // load a module from the Standard Library:
  var http = require('sjs:http');

  // load from file URL (works on server only):
  var mymodule = require("file:///Users/alex/mymodule");

  // load a module from nodejs path (server only):
  var mynodemodule = require("nodejs:mynodemodule");

  // load a built-in nodejs module (server only):
  var fs = require("nodejs:fs");
  var fs = require("fs");

Modules will be loaded once during the lifetime of the program; subsequent require calls to the same module will return the cached exports object. To get information about which modules are currently loaded, where they were required from, etc., you can inspect the require.modules object.

StratifiedJS Standard Module Library

StratifiedJS comes with a set of modules called the "StratifiedJS Standard Module Library". Each release of StratifiedJS is paired with a matching release of the Module Library. The APIs of all modules of the current stable release are documented at conductance.io/reference.

You can load modules from the Standard Library modules by using the sjs: scheme, e.g.:

  var http = require('sjs:http');

By default, the sjs: scheme resolves to

In the browser:

  http://[path where 'stratified.js' was loaded from]/modules/

On the server:

  file://[path to 'sjs' executable]/modules/

You can override these locations; see "Module resolution", below.

Module resolution

require accepts module identifiers that are relative or absolute URLs. If the URL does not end contain an extension extension, require will append an '.sjs' extension automatically.

The way module identifiers are resolved can be customized through the require.hubs variable. This variable is an array of [prefix, replacement_string|loader_object] pairs. For a given module identifier, StratifiedJS traverses this array in order, looking for prefix matches. Prefixes are replaced by replacement_strings until a loader_object is found. E.g. on the server, the prepopulated require.hubs array looks something like this:

[ [ 'sjs:', 'file:///Users/alex/stratifiedjs/modules/' ],
  [ 'github:', { src: [Function: github_src_loader] } ],
  [ 'http:', { src: [Function: http_src_loader] } ],
  [ 'https:', { src: [Function: http_src_loader] } ],
  [ 'file:', { src: [Function: file_src_loader] } ],
  [ 'nodejs:', { loader: [Function: nodejs_loader] } ] ]

A request to "sjs:http" will resolve to "file:///Users/alex/stratifiedjs/modules/http". This new URL matches the file: prefix, for which the require.hubs array contains a loader_object entry specifying that the source code should be loaded via the built-in file_src_loader function.

To map sjs: modules to a different location, you can replace the pre-populated entry in require.hubs, or just prepend a new pair, e.g.:

require.hubs.unshift(["sjs:",
                      "http://mydomain.com/sjs-mirror/"]);

// all modules addresses as 'sjs:' will now be loaded from
// the location above.

There is also a module-local require.alias variable, which performs prefix replacement similar to require.hubs, but is only applies to the current module:

require.alias.mymodules = "http://code.mydomain.com/modules/";
var mymodule = require("mymodules:mymodule");

There is also a facility for hooking external compilers (like CoffeeScript) into the require mechanism. See this Google Groups post for details.

Loading modules from HTTP servers

A call such as require('./foo') will cause the browser to make a request of the form:

http://the_server.com/foo.sjs?format=compiled

The parameter format=compiled indicates that the browser will accept a server-side compiled module. The server can safely ignore this format flag and just return the literal file 'foo.sjs'. In this way, modules can be served up by any server capable of serving static files.

Server-side SJS compilation is natively (and transparently) supported by the Conductance web application server.

Cross-domain module loading on browsers

The standard builtin module retrieval system is capable of cross-domain loading of modules (i.e. where the module's URL differs from the domain of the document performing the require), on modern browsers ( >IE6).

For this mechanism to work on modern browsers, the webserver hosting the modules needs to be configured to send CORS access control headers.

Loading GitHub modules

Note: As of June 2012, loading modules from GitHub no longer works in StratifiedJS 0.13 or earlier. It does, however, work in later versions.

In StratifiedJS 0.12 and greater, require.hubs is pre-configured to load modules with a 'github:' prefix directly from GitHub (much like the 'sjs:' prefix is configured to load from the canonical Standard Module Library location - see above). The syntax looks like this:

require("github:/USER/REPO/BRANCH_OR_TAG/MODULE_PATH");

E.g. to load the module https://github.com/afri/testmodules/blob/master/foo.sjs and call its hello function, you could write:

var foo = require("github:/afri/testmodules/master/foo");
foo.hello();

The GitHub module loading process works cross-browser and without any intermediate proxies. The browser talks directly to the GitHub API using JSONP-style requests.

The loading functionality also works transitively. I.e. if you load a module from GitHub that in turn references another module through a relative url (e.g. require('./anothermodule')), it will load fine through this mechanism.

This GitHub functionality comes in pretty handy for those situations where you just want to quickly try out a 3rd party module, or if you want to share modules during development without constantly having to upload to a webserver. In fact it works so well, it can be used in (small-scale) production as well. E.g. the xlate Chrome extension uses it to pull in a sjs4chromeapps support script:

var tabutil = require('github:/afri/sjs4chromeapps/master/tab-util');

Module Guidelines

If you're building modules that you intend to publish and allow others to use, we ask that you follow our Module Guidelines wherever possible. This allows users to integrate your modules into their application with minimal effort.

Interaction with normal JS

You can develop in StratifiedJS just like you would in conventional JavaScript. StratifiedJS is just a superset of JavaScript, so most JavaScript programs should work just fine in StratifiedJS. You can also freely call JS code (such as your favorite UI libraries) from SJS and vice versa.

Note that SJS when called from JS might not return what you expect. If the SJS function suspends while being called from JS, it will return a continuation object; not it's actual return value. That's the expected behaviour: Normal JS code cannot suspend and wait for the actual return value - this is one of the reasons for having SJS in the first place! See this Google Groups post for a mechanism of getting normal JS code to wait for SJS functions.