Securable Modules

  • Revision slug: CommonJS/Modules/Securable_Modules
  • Revision title: Securable Modules
  • Revision id: 93400
  • Created:
  • Creator: Petermichaux
  • Is current revision? No
  • Comment no wording changes

Revision Content

This specification addresses how modules should be written in order to be interoperable among a class of module systems that can be both client and server side, secure or insecure, implemented today or supported by future systems with syntax extensions.  These modules are offered privacy of their top scope, facility for importing singleton objects from other modules, and exporting their own API.

  • A module receives a "require" function.  The "require" function accepts a module identifier.  "require" returns an object containing the exported API of the foreign module.  If there is a dependency cycle, the foreign module may not have finished executing at the time it is required by one of its transitive dependencies; in this case, the object returned by "require" must contain at least the exports that the foreign module has prepared before the call to require that led to the current module's execution.  If the requested module cannot be returned, "require" throws an error.
  • A module receives an "exports" object that it may add its exported API to as it executes.
  • Interoperable modules must use the exports object as the only means of exporting, since an implementation may prevent tampering with any other object shared among modules.

To be interoperable with secure environments, a module must satisfy the following additional constraints:

  • A module must not have any free variables apart from primordials ("Object", "Array", etc.), "require", and "exports".
  • A module must not tamper with (assign to, assign to members of, delete, or otherwise mutate) the transitive primordials, the "require" object, or any object returned by "require".

This specification leaves the following important points of interoperability unspecified:

  • The domain of module identifiers.
  • Whether relative module identifiers are supported.
  • Whether a PATH is supported by the module loader for resolving module identifiers.

Sample code

// ==================================================================
// source code ------------------------------------------------------

//
// math.js
//
export.add = function() {
var sum = arguments[0];
for (var i=1; i<arguments.length; i++) {
sum += arguments[i]; }
return sum;
};

//
// increment.js
//
var add = require('math').add;
export.increment = function(val) {
add(val, 1);
};

//
// program.js
//
var inc = require('increment').increment;
var a = 1;
inc(a); // 2


// ==================================================================
// browser code in development

// locked into on of these options
//
// 1) using a special XHR synchronous script loader and // less than ideal error messages with strange line numbering
//
// 2) edit-compile-load-test cycle using the code below. Also
// strange numbering in error messages that does not match
// source files. Tools could make compile automatic but that
// means tools are required and that is not the case in
// the current browser scripting world.


// ==================================================================
// compiled for production in browser

//
// library.js
//
var require = (function() {

// memoized export objects
var exportObjects = {}

// don't want outsider redefining "require" and don't want
// to use arguments.callee so name the function here.
var require = function(name) {
if (exportsObject.hasOwnProperty(name)) {
return exportsObject[name];
}
var exports = {};
// memoize before executing module for cyclic dependencies
exportsObject[name] = exports;
modules[name](require, exports);
return exports;
};

return require;
})();

var run = function(name) {
require(name); // doesn't return exports
};

var modules = {};

//
// compiledModules.js
//
modules["math"] = function(require, exports) {
export.add = function() {
var sum = arguments[0];
for (var i=1; i<arguments.length; i++) {
sum += arguments[i];
}
return sum;
};
};

modules["increment"] = function(require, exports) {
var add = require('math').add;
export.increment = function(val) {
add(val, 1);
};
};

modules["program"] = function(require, exports) {
var inc = requrie('increment').increment;
var a = 1;
inc(a); // 2
};

//
// html in document head
//
<script src="library.js" type="text/javascript"></script>
<script src="compiledModules.js" type="text/javascript"></script>
<script type="text/javascript">
// You might not use use the window.onload property
// but rather addEventListener/attachEvent.
window.onload = function() { run("program"); };
</script>

 

Revision Source

<p>This specification addresses how modules should be written in order to be interoperable among a class of module systems that can be both client and server side, secure or insecure, implemented today or supported by future systems with syntax extensions.  These modules are offered privacy of their top scope, facility for importing singleton objects from other modules, and exporting their own API.</p>
<ul> <li>A module receives a "require" function.  The "require" function accepts a module identifier.  "require" returns an object containing the exported API of the foreign module.  If there is a dependency cycle, the foreign module may not have finished executing at the time it is required by one of its transitive dependencies; in this case, the object returned by "require" must contain at least the exports that the foreign module has prepared before the call to require that led to the current module's execution.  If the requested module cannot be returned, "require" throws an error.</li> <li>A module receives an "exports" object that it may add its exported API to as it executes.</li> <li>Interoperable modules must use the exports object as the only means of exporting, since an implementation may prevent tampering with any other object shared among modules.</li>
</ul>
<p>To be interoperable with secure environments, a module must satisfy the following additional constraints:</p>
<ul> <li>A module must not have any free variables apart from primordials ("Object", "Array", etc.), "require", and "exports".</li> <li>A module must not tamper with (assign to, assign to members of, delete, or otherwise mutate) the transitive primordials, the "require" object, or any object returned by "require".</li>
</ul>
<p>This specification leaves the following important points of interoperability unspecified:</p>
<ul> <li>The domain of module identifiers.</li> <li>Whether relative module identifiers are supported.</li> <li>Whether a PATH is supported by the module loader for resolving module identifiers.</li>
</ul>
<h3>Sample code</h3>
<pre><code>// ==================================================================<br>// source code ------------------------------------------------------<br><br>//<br>// math.js<br>//<br>export.add = function() {<br>  var sum = arguments[0];<br>  for (var i=1; i&lt;arguments.length; i++) {<br>    sum += arguments[i];   }<br>  return sum;<br>};<br><br>//<br>// increment.js<br>//<br>var add = require('math').add;<br>export.increment = function(val) {<br>  add(val, 1);<br>};<br><br>//<br>// program.js<br>//<br>var inc = require('increment').increment;<br>var a = 1;<br>inc(a); // 2<br><br><br>// ==================================================================<br>// browser code in development<br><br>// locked into on of these options<br>//<br>//  1) using a special XHR synchronous script loader and    //     less than ideal error messages with strange line numbering<br>//<br>//  2) edit-compile-load-test cycle using the code below. Also<br>//     strange numbering in error messages that does not match<br>//     source files. Tools could make compile automatic but that<br>//     means tools are required and that is not the case in <br>//     the current browser scripting world.<br><br><br>// ==================================================================<br>// compiled for production in browser<br><br>//<br>// library.js<br>//<br>var require = (function() {<br>  <br>  // memoized export objects<br>  var exportObjects = {}<br><br>  // don't want outsider redefining "require" and don't want<br>  // to use arguments.callee so name the function here.<br>  var require = function(name) {<br>    if (exportsObject.hasOwnProperty(name)) {<br>      return exportsObject[name];<br>    }<br>    var exports = {};<br>    // memoize before executing module for cyclic dependencies<br>    exportsObject[name] = exports;<br>    modules[name](require, exports);<br>    return exports;<br>  };<br>  <br>  return require;<br>})();<br><br>var run = function(name) {<br>  require(name); // doesn't return exports<br>};<br><br>var modules = {};<br><br>//<br>// compiledModules.js<br>//<br>modules["math"] = function(require, exports) {<br>  export.add = function() {<br>    var sum = arguments[0];<br>  	for (var i=1; i&lt;arguments.length; i++) {<br>  	  sum += arguments[i];<br>  	}<br>  	return sum;<br>  };<br>};<br><br>modules["increment"] = function(require, exports) {<br>  var add = require('math').add;<br>  export.increment = function(val) {<br>    add(val, 1);<br>  };<br>};<br><br>modules["program"] = function(require, exports) {<br>  var inc = requrie('increment').increment;<br>  var a = 1;<br>  inc(a); // 2<br>};<br><br>//<br>// html in document head<br>//<br>&lt;script src="library.js" type="text/javascript"&gt;&lt;/script&gt;<br>&lt;script src="compiledModules.js" type="text/javascript"&gt;&lt;/script&gt;<br>&lt;script type="text/javascript"&gt;<br>  // You might not use use the window.onload property<br>  // but rather addEventListener/attachEvent.<br>  window.onload = function() {     run("program");   };<br>&lt;/script&gt;<br><br></code></pre>
<p> </p>
Revert to this revision