Stratified JavaScript

Bringing JavaScript into the modern age.
Today.
For every browser & nodejs.

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.

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

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.

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:

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:

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:

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:

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.

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).

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:

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.

About Oni Labs

We're a young startup bringing rocket science to the world of web apps.
Interested in working with us? Get in touch.

Our mission is to make the tools and technologies developers need to create fast & scalable web applications.