diff --git a/.flowconfig b/.flowconfig new file mode 100644 index 0000000..f66e76e --- /dev/null +++ b/.flowconfig @@ -0,0 +1,7 @@ +[libs] +interfaces/ + +[ignore] +.*node_modules/.*/test/.*\.json +.*node_modules/.*/tables/.*\.json +.*node_modules/resolve/lib/.*\.json diff --git a/.gitignore b/.gitignore index e2a7dcb..2e19ce7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +capper.config + ################# ## Eclipse ################# diff --git a/README.md b/README.md index aabb504..7abf1ba 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ or which will do the same thing. -Next, you must modify the capper.config file to suit your needs. To conduct client-server testing on your development machine, you probably want to set "domain" to "localhost"; it will certainly not work if you leave the domain unchanged. Select a port you can open on your machine (beware of firewalls blocking ports, you may have to configure the firewall as well). At this time you must use protocol https. The self-signed certificate embodied in the "ssl" folder will be adequate for simple testing, though you will have to click OK through the cert monolog boxes in your browser. +Next, you must make a `capper.config` file to suit your needs (se `capper.config.example` as a pattern). To conduct client-server testing on your development machine, you probably want to set "domain" to "localhost"; it will certainly not work if you leave the domain unchanged. Select a port you can open on your machine (beware of firewalls blocking ports, you may have to configure the firewall as well). At this time you must use protocol https. The self-signed certificate embodied in the "ssl" folder will be adequate for simple testing, though you will have to click OK through the cert monolog boxes in your browser. In a command window, change directories to the Capper folder and startup the server from the command line with >node --harmony-proxies server diff --git a/apps/sealer/server/main.js b/apps/sealer/server/main.js index 5405d75..8785643 100755 --- a/apps/sealer/server/main.js +++ b/apps/sealer/server/main.js @@ -2,6 +2,7 @@ "use strict"; var caplib = require("../../../caplib"); +var unique = caplib.makeUnique(require("crypto").randomBytes); module.exports = Object.freeze({ makeSealerPair: function(context) { return Object.freeze({ @@ -16,33 +17,33 @@ module.exports = Object.freeze({ makeSealer: function(context) { var mem = context.state; var self; - self = Object.freeze({ + self = Object.freeze({ init: function(share) { if ("share" in mem) {return;} //already initialized mem.share = share; }, seal: function(x) { - var box = caplib.unique(); + var box = unique(); mem.share.state[box] = x; return box; } - }); + }); return self; }, makeUnsealer: function(context) { var mem = context.state; var self; - self = Object.freeze({ + self = Object.freeze({ init: function(share) { if ("share" in mem) {return;} //already initialized mem.share = share; }, - unseal: function(box) { + unseal: function(box) { var x = mem.share.state[box]; // delete mem.share.state[box] //uncomment to create an open-only-once unsealer return x; } - }); + }); return self; } -}); \ No newline at end of file +}); diff --git a/caplib.js b/caplib.js index f5cab22..acf752c 100644 --- a/caplib.js +++ b/caplib.js @@ -1,4 +1,4 @@ -/* +/* Copyright 2011 Hewlett-Packard Company. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software @@ -12,17 +12,17 @@ with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Please contact the Hewlett-Packard Company for information regarding how to obtain the source code for this library. + + @flow */ -/*global console, require, module */ -module.exports = function() { +/*global console, module */ +/* jshint esversion: 6, node: true */ + "use strict"; -var fs = require("fs"); -var path = require("path"); -var crypto = require("crypto"); /** * Function typeCheck allows a quick, concise limited form of duck typing of the - * arguments to a function. The function using typeCheck should pass in its + * arguments to a function. The function using typeCheck should pass in its * arguments plus a character specifying a type for each argument to be * tested. The types are: * { @@ -41,57 +41,64 @@ var crypto = require("crypto"); * If the first two arguments to add were not numbers, typeCheck logs to console and returns false. * * Additional rules: - * Duck will throw an exception if there are more tests than arguments. + * Duck will throw an exception if there are more tests than arguments. * However, if there are additional arguments for which there are not tests, it will pass. * Duck will throw if it gets a null or undefined for any argument. **/ -function typeCheck(args, validTypes) { +function typeCheck(args /*:Array*/, validTypes /*: string*/) /*:bool*/ { var types = { n: "number", s: "string", f: "function", o: "object", b: "boolean" - }; + }; //you can have untested args, but not tests for which there are no args - if (args.length < validTypes.length) {throw "typeCheck has more tests than args";} + if (args.length < validTypes.length) {throw "typeCheck has more tests than args";} for (var i = 0; i < validTypes.length; i++) { if (args[i] === null) {console.log("typeCheck found null"); return false;} var typ = validTypes[i]; if (typeof(args[i]) !== types[typ]) { - console.log("typeCheck bad arg " + i + " should be: " + typ + - " was " + typeof(args[i])); + console.log("typeCheck bad arg " + i + " should be: " + typ + + " was " + typeof(args[i])); return false; } } return true; } -function valid(test, errMsg) { +function valid(test /*: any*/, errMsg /*: string*/) /*: bool*/ { if (test) {return true;} console.log("invalid: " + errMsg); throw("invalid: " + errMsg); } -function tvalid(args, types, additionalTest, errMsg) { +function tvalid(args /*:Array*/, types /*: string*/, additionalTest /*:bool*/, errMsg /*: string*/) /*:bool*/ { return valid(typeCheck(args, types) && additionalTest, errMsg); } -function unique() { - var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; - var ans = ""; - var buf = crypto.randomBytes(25); - for (var i = 0; i < buf.length; i++) { - var index = buf[i] % chars.length; - ans += chars[index]; +function makeUnique(randomBytes /*: (qty: number) => Array*/ + ) /*: () => string*/{ + + function unique() { + var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; + var ans = ""; + var buf = randomBytes(25); + for (var i = 0; i < buf.length; i++) { + var index = buf[i] % chars.length; + ans += chars[index]; + } + //while (ans.length < 30) { + // var nextI = Math.floor(Math.random()*10000) % chars.length; + // ans += chars[nextI]; + //} + return ans; } - //while (ans.length < 30) { - // var nextI = Math.floor(Math.random()*10000) % chars.length; - // ans += chars[nextI]; - //} - return ans; + + return unique; } -function makeSealerPair() { + +function makeSealerPair() /*: SealerPair*/{ var holder = null; function seal(x) { var box = function() {holder = x;}; @@ -107,26 +114,29 @@ function makeSealerPair() { return {seal:seal, unseal:unseal}; } -function cloneMap(m) { +function cloneMap(m /*: Object*/) /*: Object*/{ var clone = {}; for (var key in m) {clone[key] = m[key];} return clone; } /** * If the obj is a persistent object, or if the obj contains a ref to a persistent - * object, or the id of a persistent obj ({"@id": cred}), replace the object with + * object, or the id of a persistent obj ({"@id": cred}), replace the object with * a webkey object (i.e., {"@" : webkey}). If * a function is encountered, convert to null and log it. In all other cases, * leave it alone. - * + * * In the end, return the json string of the result. - * + * * TODO: the in-place replacement makes it possible for the persistent app * that produced this array or map to see the webkeys of the live * objects it included. Unnecessary loss of encapsulation. * Do copy, not in-place replace. * */ -function deepObjToJSON(obj, idToWebkey, saver) { +function deepObjToJSON(obj /*:any*/, + idToWebkey /*: (id: Object) => string*/, + saver /*: Saver*/) /*: any */ +{ if (obj === undefined) {return null;} if (obj === null) {return null;} if (typeof obj === "function") { @@ -137,86 +147,69 @@ function deepObjToJSON(obj, idToWebkey, saver) { if (saver.hasId(obj)) { var webkey = idToWebkey(saver.asId(obj)); return {"@" : webkey}; - } + } if (saver.live(obj)) {return {"@": idToWebkey(obj)};} - var clone = obj instanceof Array ? [] : {}; + var clone /*: any*/= obj instanceof Array ? [] : {}; for (var key in obj) { clone[key] = deepObjToJSON(obj[key], idToWebkey, saver); } return clone; } + /** - * finding commands such as drop and make, if a command line has an arg + * finding commands such as drop and make, if a command line has an arg * after "server" make the rest of the args elements in map - * Returns map with one entry, the first post=server element as key, + * Returns map with one entry, the first post=server element as key, * following args as list * Converts args preceded with "#" to numbers * Converts args preceded with "@" to live refs * */ -function argMap(argv, webkeyStringToLive) { - var args = {}; +function argMap(argv /*: Array*/, + webkeyStringToLive /*: (webkey: string) => any */) /*: Object*/ +{ + var args = {}; var serverIndex; - for (serverIndex = 0; - serverIndex < argv.length && argv[serverIndex].indexOf ("server") < 0; - serverIndex++) {} + for (serverIndex = 0; + serverIndex < argv.length && argv[serverIndex].indexOf ("server") < 0; + serverIndex++) { + // next arg... + } if (serverIndex === argv.length - 1) {return args;} + var parseArg = makeParseArg(webkeyStringToLive); var command = argv[serverIndex+1]; - var commandArgs = argv.splice(serverIndex+2); - commandArgs.forEach(function(next, i) { - if (next.indexOf("#") === 0) { - commandArgs[i] = parseFloat(next.substring(1)); - } else if (next.indexOf("@") === 0) { - commandArgs[i] = webkeyStringToLive(next.substring(1)); - } + var commandArgs /*: Array*/ = []; + argv.splice(serverIndex+2).forEach(function(next, i) { + commandArgs[i] = parseArg(next); }); args[command] = commandArgs; return args; } -function copyFile(src, dest) { - var data = fs.readFileSync(src); - fs.writeFileSync(dest); -} -function copyRecurse(src, dest) { - var exists = fs.existsSync(src); - var stats = exists && fs.statSync(src); - var isDirectory = exists && stats.isDirectory(); - if (exists && isDirectory) { - fs.mkdirSync(dest); - fs.readdirSync(src).forEach(function(childItemName) { - copyRecurse(path.join(src, childItemName), - path.join(dest, childItemName)); - }); - } else if (exists) {copyFile(src, dest);} -} -function makeNewServer() { - function copyIfAbsent(dest) { - var srcDir = path.dirname(module.filename); - if (!fs.existsSync(dest)) { - copyRecurse(path.join(srcDir, dest), dest); +function makeParseArg(webkeyStringToLive /*: (webkey: string) => any */) /*: (string) => any */{ + return parseArg; + + function parseArg(next /*: string*/) { + if (next.indexOf("#") === 0) { + return parseFloat(next.substring(1)); + } else if (next.indexOf("@") === 0) { + return webkeyStringToLive(next.substring(1)); } + return next; } - copyIfAbsent("capper.db"); - copyIfAbsent("capper.config"); - copyIfAbsent("apps"); - copyIfAbsent("test"); - copyIfAbsent("views"); - copyIfAbsent("ssl"); } + var self = { typeCheck: typeCheck, - unique: unique, + makeUnique: makeUnique, valid: valid, tvalid: tvalid, makeSealerPair: makeSealerPair, cloneMap: cloneMap, - argMap:argMap, - deepObjToJSON: deepObjToJSON, - makeNewServer: makeNewServer, - copyRecurse: copyRecurse + argMap: argMap, + makeParseArg: makeParseArg, + deepObjToJSON: deepObjToJSON }; -return self; -}(); \ No newline at end of file +module.exports = self; diff --git a/capper.config b/capper.config.example similarity index 100% rename from capper.config rename to capper.config.example diff --git a/capper.db b/capper.db deleted file mode 100644 index a0be124..0000000 --- a/capper.db +++ /dev/null @@ -1 +0,0 @@ -{"bRDBa6PJLBf77SWxkbEoeUvde6znVr":{"data":{"y":"persistdata","a":1},"reviver":"shared"},"mnYQh0Nwg3heNxqdRmkmi8xONn8GoW":{"data":{},"reviver":"money.makeMint"},"_E7RQowGAs7dd9prpUHcFhE2nUdWcN":{"data":{},"reviver":"shared"},"8Qg41fQzSjEvRjgOvhollzzE1z8JmH":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":999869},"reviver":"money.makePurse"},"0AR89dJOdlmod62YmsVtWrdNNiH3qB":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"1DzhT3QJ_SYZMfBDhHN8JgrhO1kubt":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":20},"reviver":"money.makePurse"},"7mVHM7n1PnBFfXO0iRNYTMR7RhdHiZ":{"data":{"x":52},"reviver":"incr"},"Tye3FlHT4NejDbm4vxa2rtKZVcSuKE":{"data":{"x":0},"reviver":"incr"},"SSW5M3oDtNcaB1VHj2oqFy0iPWAYrO":{"data":{"x":0},"reviver":"incr"},"kuDlDGahvO1EDEdT_xGgQec7b5celv":{"data":{"x":0},"reviver":"incr"},"9cq_pvJyJu2IhbxKaBtsEsYSTt2Ja5":{"data":{"x":0},"reviver":"incr"},"2wMCuaNpxOJbGAVkVn0W2XHH_MyaeC":{"data":{"x":2},"reviver":"incr"},"xXjhgpiR_KIeUCOW5_Gj2clqJ6LtKH":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"kjyJugpazF7P26c8Xu32Scz5_Vifdh":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"IwpYrSClWsvAJJ_EWSYPwsWRgYCPqt":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"i73_v1kYyZpPZpgJpA5DaXOt5lMyO9":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":11},"reviver":"money.makePurse"},"Gwh7LaWn5msdnQg_4ergA3debMdeVK":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":12},"reviver":"money.makePurse"},"SjhlHpCVM46oEFgRU1uGkW7LpPPFiE":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"aN9YGPxkRxBbK1BFo2lXmSJQwUqyAI":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":null},"reviver":"money.makePurse"},"aZD2nRaeAd4lMI3VY_fuk4xLRlFEyC":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"_rsrRVahhJaRw61sDBtklIdHmr4_oy":{"data":{},"reviver":"money.makePurse"},"rySCXyMEhEyPG0uTl1iX91neGuG8o8":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"KeAFdoDcuPaGcI2JtsnajTNgt8ThyB":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"DCDoNb2KFBW9kPxxYlGF6pJmKCvo4p":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"nGnMxaCDJ8bRuZvYv5Q6XDPmsbRhAa":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"JlBAJ317gdQ3dkyuXSjuM6WxGbUbw8":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":60},"reviver":"money.makePurse"},"wTnxFF3F5tOjPS_hYFcWMxdIPmkh1b":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"JRN2xGEmTIbVhPMPRvItMZjq8XFqDq":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"dXwZsrHDyU7plhQOjipSTMmuxBCj62":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":10},"reviver":"money.makePurse"},"Ws1Wyr6lL6KCnWmoablN0CC43XKBXA":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"vVsHmHii8xK8wgVg03SmtHmLolq01m":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"27uaFkrzmAbeBw7UoW2zUyFEQhluBD":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":10},"reviver":"money.makePurse"},"_JLe5fL_fU_Zs0Zpt2hvR8SQZNLlet":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"oEJhNe1filxgl2mAbsyvXdvM9KA4qB":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"2mgb4bY3rnk6oYWcKlf8hds_m4Uubp":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"yBTfm7fYxOuGqtO9SisIVDxg3bnobj":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"_ppstPiBrl1hGk4gveYs1wpcLG9KgA":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"MDbsdhNDai6AsE0cIyPrMRbem6Vrw7":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"UfwGLXaMMiPImHqCkgBQIEwE8iZvbW":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"rptEe8oF7i8_x5tc0T_ue48N2Zw7VX":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"ZFkdkce3JuFthAVhRI59CECz93zI5d":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":10},"reviver":"money.makePurse"},"Ld4RmC_pbpIuZ3en5bvtUSS3Wjt_lo":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":10},"reviver":"money.makePurse"},"yspuEyW0nFwC0DLq56ZnEcWlkFKfBF":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"9l7OAl2WhlYGCU4Gt4fC2jeruRYPTs":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"OGkNJ8ddUJfBzPV8_nz2xC5UM":{"data":{"x":0},"reviver":"incr"},"nU0sS9t5QehCI5VpAWJxE7LSl":{"data":{"x":6},"reviver":"incr"},"QuEcteIm9G3xCOLN86Z_ls5kK":{"data":{},"reviver":"shared"},"Ma5AVrGyUIApD93iL0M9S0Ah7":{"data":{},"reviver":"shared"},"9Y_AR561uOBpkUAVeBVdV_cze":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"4pOak_GE1AcfpPw_0vJuYP4ya":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"KeIEIxQklPoT1yG817xc8FQcA":{"data":{"x":1},"reviver":"incr"},"cQHHJ5zVw6An4_olXNdpI3OF4":{"data":{"x":1},"reviver":"incr"},"p4PkUaFylsvJsUG5d8HWhqPI0":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"oVVj4cX0P1TMtaTVNWaOCLxfq":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"},"Vs6Q6ofuVL_DzzqoYe7cEuO4G":{"data":{"greeting":"Hello World"},"reviver":"hello"},"qKXeaEbCLmgwKeNJEbz6SmOby":{"data":{"sold":2,"register":{"@id":"wdWXn9WUKBrxa7xfmHWwezsI7"}},"reviver":"cookieShop"},"wdWXn9WUKBrxa7xfmHWwezsI7":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":6},"reviver":"money.makePurse"},"pVAwEoKrMkznsC6KaD6aXEYgS":{"data":{"share":{"@id":"_E7RQowGAs7dd9prpUHcFhE2nUdWcN"},"balance":0},"reviver":"money.makePurse"}} \ No newline at end of file diff --git a/interfaces/express.js b/interfaces/express.js new file mode 100644 index 0000000..1dbe873 --- /dev/null +++ b/interfaces/express.js @@ -0,0 +1,15 @@ +type Application = any; + +/* TODO: +type Application = { + get(route: string, ...handers: RequestHandler[]): void; +}; +*/ + +type RequestHandler = (req: Request, res: Response, next: NextFunction) => any; +type NextFunction = (err: any) => void; +type Request = { }; +type Response = { + sendfile(path: string): void; +} + diff --git a/interfaces/node.js b/interfaces/node.js new file mode 100644 index 0000000..e282ad4 --- /dev/null +++ b/interfaces/node.js @@ -0,0 +1,52 @@ +// cribbed from +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/node/node.d.ts +type FileSystem = { + existsSync(path: string): boolean; + readFile(filename: string, encoding: string, + callback: (err: ?Error, data: string) => void): void; + readFileSync(filename: string, options?: T): U; + writeFileSync(filename: string, data: string | Buffer, options?: { encoding?: string; mode?: number; flag?: string; }): void; + writeFile(filename: string, + data: Buffer | string, + options?: Object | string, + callback?: (err: ?Error) => void + ): void; + mkdirSync(path: string, mode?: number): void; + readdirSync(path: string): string[]; + statSync(path: string): Stats; +} + +type Buffer = { + length: number +} + +type Path = { + join(...paths: any[]): string; + dirname(p: string): string; +} + +type Stats = { + isFile(): boolean; + isDirectory(): boolean; + isBlockDevice(): boolean; + isCharacterDevice(): boolean; + isSymbolicLink(): boolean; + isFIFO(): boolean; + isSocket(): boolean; + dev: number; + ino: number; + mode: number; + nlink: number; + uid: number; + gid: number; + rdev: number; + size: number; + blksize: number; + blocks: number; + atime: Date; + mtime: Date; + ctime: Date; + birthtime: Date; +} + +type ErrnoException = any; diff --git a/interfaces/types.js b/interfaces/types.js new file mode 100644 index 0000000..185caf2 --- /dev/null +++ b/interfaces/types.js @@ -0,0 +1,46 @@ +type Reviver = { + toMaker(reviver: string): any, + sendUI(res: Response, reviver: string): void; +}; + +type Id = {id: string} + +type Saver = { + deliver(id: Id, method: string, ...optArgs: Array): Promise; + make(makerLocation: string, optInitArgs: ?Array): Object; + reviver(id: Id): string; + hasId(ref: any): bool; + asId(ref: any): Object; + idToCred(id: Id): string; + credToId(cred: string): Id; + live(id: Id): any; + checkpoint(): Promise, + drop(id: Id): void; +}; + + +type ReadAccess = { + readBytes(): Promise; + readText(encoding: string): Promise; + subRd(other: string): ReadAccess; +} + + +type WriteAccess = { + writeText(text: string): Promise; + ro(): ReadAccess; +} + + +type SyncAccess = { + existsSync(): boolean; + readTextSync(encoding: string): string; + writeSync(text: string): void; + unsync(): WriteAccess; +} + + +type SealerPair = { + seal(x: T): any; + unseal(boxy: any): T +} diff --git a/package.json b/package.json index 49dd96d..8b025e3 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,45 @@ "express": "~3.4.1" }, "devDependencies": { + "eslint": "^2.2.0", "mocha": "~1.19.0" + }, + "eslintConfig": { + "ecmaVersion": 6, + "extends": "eslint:recommended", + "rules": { + "indent": [ + 2, + 4 + ], + "no-trailing-spaces": 2, + "quotes": [ + 1, + "double" + ], + "no-console": 0, + "brace-style": [ + 1, + "1tbs", + { + "allowSingleLine": true + } + ], + "block-spacing": [ + 2, + "never" + ], + "array-bracket-spacing": [ + 2, + "never" + ], + "no-unused-vars": [ + 2, + { + "args": "after-used", + "argsIgnorePattern": "^_" + } + ] + } } } diff --git a/saver.js b/saver.js index 04d97db..1d8b874 100644 --- a/saver.js +++ b/saver.js @@ -1,4 +1,4 @@ -/* +/* Copyright 2011 Hewlett-Packard Company. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software @@ -12,41 +12,46 @@ with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Please contact the Hewlett-Packard Company for information regarding how to obtain the source code for this library. + +@flow */ -/*global require, Map, console, Proxy, setImmediate */ -var fs = require("fs"); +/*global require, Map, console, Proxy, setImmediate, __dirname */ +/* eslint-env es6, node */ +"use strict"; var Q = require("q"); var caplib = require("./caplib"); -module.exports = function(){ - "use strict"; - var dbfile = "capper.db"; +exports.makeSaver = makeSaver; +function makeSaver(unique /*: () => string*/, + dbfile /*: SyncAccess*/, + reviverToMaker /*: (path: string) => any*/) /*: Saver */ +{ function log(text) {console.log(text);} - + // modifiedObjs has a cred as key and null as value; it is a set var modifiedObjs = {}; var checkpoint; var make; var live; - + /** * Generally, a turn begins when a message is delivered from the server, * and at the end of the turn, saver automatically initiates a checkpoint * and gives the server a promise for the answer. The answer is fulfilled * when the checkpoint is complete, and the server can then ship the answer * back to the client. - * - * However, if an app object internally uses mechanism like - * setTimeout or NextTick or an internal promise to make something happen - * later, the checkpointing system has no way of knowing about that new - * turn that has been queued. - * + * + * However, if an app object internally uses mechanism like + * setTimeout or NextTick or an internal promise to make something happen + * later, the checkpointing system has no way of knowing about that new + * turn that has been queued. + * * For the moment, the solution is as follows: any time a persistent object * changes its context.state, we look at the checkpointNeeded flag. If false, * we set it to true and setImmediate a checkpointIfNeeded. The checkpoint * function itself clears the flag. If state is changed during a normal * method invocation turn, the checkpointIfNeeded will find the flag already - * cleared by the automatic checkpoint by the time its turn fires. If - * state is changed during a + * cleared by the automatic checkpoint by the time its turn fires. If + * state is changed during a * timer event, the checkPointIfNeeded takes care of it. * * To allow the checkpointer to not checkpoint if no state changed, @@ -55,49 +60,51 @@ module.exports = function(){ var lastCheckpointQueued = new Q(true); var checkpointNeeded = false; function checkpointIfNeeded() { - if (checkpointNeeded){ - checkpoint();} + if (checkpointNeeded) { + checkpoint(); + } } function setupCheckpoint() { if (!checkpointNeeded) { checkpointNeeded = true; setImmediate(checkpointIfNeeded); - } + } } - - function rawSend(webkey, method, args) {log("no send implemented");} - function credToId(cred) {return Object.freeze({"@id": cred }); } + + function credToId(cred) {return Object.freeze({"@id": cred });} function idToCred(id) {return id["@id"];} - - /** - * systate keys are the credentials from the webkeys, i.e., from the "ids". - * each value is a map with - * data: the pure data for reviving the object - * reviver: an array of strings for finding the function to revive it - * array[0] is the path to the code to be loaded via require - * array[1] is the optional method to invoke - **/ - var sysState = {}; - function loadSysState(){ - if (!fs.existsSync(dbfile)) {fs.writeFileSync(dbfile, "{}");} - var jsonState = fs.readFileSync(dbfile, 'utf8'); + + /** + * systate keys are the credentials from the webkeys, i.e., from the "ids". + * each value is a map with + * data: the pure data for reviving the object + * reviver: an array of strings for finding the function to revive it + * array[0] is the path to the code to be loaded via require + * array[1] is the optional method to invoke + **/ + var sysState = {}; + function loadSysState() { + if (!dbfile.existsSync()) {dbfile.writeSync("{}");} + var jsonState = dbfile.readTextSync("utf8"); if (jsonState.length === 0) {jsonState = "{}";} sysState = JSON.parse(jsonState); - } - loadSysState(); + } + loadSysState(); var validStateObj; checkpoint = function() { var vowPair = Q.defer(); for (var next in modifiedObjs) { - try { + try { checkpointNeeded = true; var initState = sysState[next].data; var state = validStateObj(initState); - if (!state) { - console.log("!!!ERROR bad function in checkpoint for object of type " + + if (!state) { + console.log("!!!ERROR bad function in checkpoint for object of type " + sysState[next].revive); } - }catch (err) {} //missing cred in modifiedObjs, probably dropped from the table + } catch (err) { + //missing cred in modifiedObjs, probably dropped from the table + } } modifiedObjs = {}; if (!checkpointNeeded) { @@ -107,59 +114,61 @@ module.exports = function(){ //force sequentiality on multiple pending checkpoints var last = lastCheckpointQueued; lastCheckpointQueued = vowPair.promise; - last.then(function(ok) { + last.then(function(_ok) { var jsonState = JSON.stringify(sysState); checkpointNeeded = false; //yes, say it again on each turn - fs.writeFile(dbfile, jsonState, function (err) { - if (err) { - console.log("checkpoint failed: " + err); - vowPair.reject(err); - } else {vowPair.resolve(true);} + var dbunsync = dbfile.unsync(); + return dbunsync.writeText(jsonState) + .then(function() {vowPair.resolve(true)}) + .catch(function (err) { + console.log("checkpoint failed: " + err); + vowPair.reject(err); + }); //log("db size after write: "+ fs.statSync("capper.db").size); - }); }); } return vowPair.promise; - }; - /* - * livestate keys are the credentials. - * each value is the reference to the corresponding object - */ - var liveState = {}; + }; + /* + * livestate keys are the credentials. + * each value is the reference to the corresponding object + */ + var liveState = {}; //each key is the ref to the live object, the value is the id that contains the cred - var liveToId = new Map(); - - function asId(ref) { - if (liveToId.has(ref)){return liveToId.get(ref); } - throw ("asId bad ref"); + var liveToId /*: Map*/ = new Map(); + + function asId(ref) /*: Object*/{ + var id = liveToId.get(ref); + if (id === undefined) throw ("asId bad ref"); + return id; } function hasId(ref) {return liveToId.has(ref);} validStateObj = function(obj) { if (obj === null) {return null;} var id = liveToId.get(obj); - if (id) {return id; } + if (id) {return id;} for (var key in obj) { var nextval = obj[key]; if (typeof nextval === "function") { console.log("!!!ERROR found function in validStateObj " + nextval); - return null; + return null; } if (typeof nextval === "object") { var childObj = validStateObj(nextval); //cannot get here unless the object is valid obj[key] = childObj; - } + } } return obj; }; function drop(id) { setupCheckpoint(); var cred = idToCred(id); - if (liveState[cred]){liveToId.delete(liveState[cred]);} + if (liveState[cred]) {liveToId.delete(liveState[cred]);} delete liveState[cred]; - delete sysState[cred]; + delete sysState[cred]; } function makePersister(id) { @@ -179,46 +188,47 @@ module.exports = function(){ } return thing; } - var p = Proxy.create({ + var p = new Proxy({}, { //TODO unwind gotten objects to replace ids with persistent objs get: function(proxy, key) { var ans = convertForExport(data[key]); if (ans && typeof ans === "object" && !hasId(ans)) { modifiedObjs[cred] = null; - } + } return ans; }, - set: function(proxy, key, val) { + set: function(proxy, key, val) { setupCheckpoint(); modifiedObjs[cred] = null; - data[key] = val; + data[key] = val; var typ = typeof(val); if (typ === "function") { - throw "Function in context.state disallowed in " + constructorLocation; + throw "Function in context.state disallowed in " + constructorLocation; } if (typ === "object") { if (val === null) { - data[key] = null; + data[key] = null; } else { var obj = validStateObj(val); if (obj) { - data[key] = obj; + data[key] = obj; } else { throw "NonPersistent Object in context.state.set disallowed in "+ constructorLocation + " badObj: " + val; } } - } else {data[key] = val;} + } else {data[key] = val;} + return true; }, - has: function(name) {return name in data;} + has: function(name /*: string */) {return name in data;} //delete, iterate, keys }); - return p; + return p; } function State(id) { return makePersister(id); } - + /* * Context is a map that contains * make(creationFn, args...) that returns a new persistent object. The @@ -229,101 +239,178 @@ module.exports = function(){ * state is a proxied map that stores the persistent instance data for this * object. Attempts to store functions or nonpersistent objects will throw exceptions. * drop() removes yourself (eventually!) from the persistence system - * - */ - function makeContext(id, constructorLocation) { - var newContext = {make: make, state: new State(id)}; - newContext.drop = function() {drop(id);}; - newContext.isPersistent = function(obj) { - var id = liveToId.get(obj); - return id !== undefined && id !== null; + * + */ + function makeContext(id, _constructorLocation) { + var newContext = { + make: make, + state: new State(id), + drop: function() {drop(id);}, + isPersistent: function(obj) { + var id = liveToId.get(obj); + return id !== undefined && id !== null; + } }; - return Object.freeze(newContext); - } - - function reviverToMaker(reviver) { - var parts = reviver.split("."); - var path = "./apps/" + parts[0] +"/server/main.js"; - var maker = require(path); - if (parts.length === 2) {maker = maker[parts[1]];} - return maker; + return Object.freeze(newContext); } - make = function(makerLocation, optInitArgs) { + + make = function(makerLocation, _optInitArgs) { setupCheckpoint(); - var cred = caplib.unique(); + var cred = unique(); var newId = credToId(cred); sysState[cred] = {data: {}, reviver: makerLocation}; - var newContext = makeContext(newId); - var maker = reviverToMaker(makerLocation); - var obj = maker(newContext); + var newContext = makeContext(newId); + var maker = reviverToMaker(makerLocation); + var obj = maker(newContext); liveState[cred] = obj; liveToId.set(obj, newId); var initArgs = []; for (var i = 1; i any*/) /*: Reviver*/ { + function toMaker(reviver) { + var parts = reviver.split("."); + var path = __dirname + "/apps/" + parts[0] +"/server/main.js"; + var maker = require(path); + if (parts.length === 2) {maker = maker[parts[1]];} + return maker; + } + + function sendUI(res, reviver, path /*: ?string*/) { + var revstring = "./apps/"; + if (path) { + revstring += "/ui" + path; + } else{ + var parts = reviver.split("."); + revstring = revstring + parts[0] + "/ui/"; + var filename = parts.length > 1 ? parts[1] : "index"; + revstring += filename + ".html"; + } + res.sendfile(revstring); + } + + return Object.freeze({ + toMaker: toMaker, + sendUI: sendUI + }); +} + +exports.ezSaver = ezSaver; +function ezSaver(require /*: (name: string) => any*/ + ) /*: { reviver: Reviver, saver: Saver }*/{ + var unique = caplib.makeUnique(require("crypto").randomBytes); + var reviver = makeReviver(require); + const dbfile = fsSyncAccess(require("fs"), + require("path").join, + "capper.db"); + return Object.freeze({ + reviver: reviver, + saver: makeSaver(unique, dbfile, reviver.toMaker) + }); +} + +exports.fsReadAccess = fsReadAccess; +function fsReadAccess(fs /*: FileSystem */, + join /*:(...parts: Array) => string*/, + path /*: string*/) /*: ReadAccess*/ { + return Object.freeze({ + readText: (encoding /*: string*/) => + Q.nfcall(fs.readFile, path, encoding), + readBytes: () => + Q.nfcall(fs.readFile, path), + subRd: (other) => fsReadAccess(fs, join, join(path, other)) + }); +} + +exports.fsWriteAccess = fsWriteAccess; +function fsWriteAccess(fs /*: FileSystem */, + join /*:(...parts: Array) => string*/, + path /*: string*/) /*: WriteAccess*/ { + return Object.freeze({ + writeText: (text) => + Q.nfcall(fs.writeFile, path, text), + subWr: (other) => fsWriteAccess(fs, join, join(path, other)), + ro: () => fsReadAccess(fs, join, path) + }); +} + +exports.fsSyncAccess = fsSyncAccess; +function fsSyncAccess(fs /*: FileSystem */, + join /*:(...parts: Array) => string*/, + path /*: string*/) /*: SyncAccess*/ { + return Object.freeze({ + existsSync: () => fs.existsSync(path), + readTextSync: (encoding) => fs.readFileSync(path, encoding), + writeSync: (contents) => fs.writeFileSync(path, contents), + unsync: () => fsWriteAccess(fs, join, path) + }); +} diff --git a/server.js b/server.js index 582d9a5..32a389a 100644 --- a/server.js +++ b/server.js @@ -1,4 +1,4 @@ -/* +/* Copyright 2011 Hewlett-Packard Company. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software @@ -12,181 +12,274 @@ with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Please contact the Hewlett-Packard Company for information regarding how to obtain the source code for this library. + + @flow */ -/*global require, console, process */ +/*global require, module, console, process, __dirname */ +/* eslint-env es6 */ "use strict"; -var https = require("https"); -var fs = require("fs"); -var express = require("express"); var Q = require("q"); var caplib = require("./caplib"); -var saver = require("./saver"); -var log = function(text) {console.log(text);}; - -var domain; -var port; -var deferStart = Q.defer(); -fs.readFile("capper.config", "utf8", function(err, data) { - if (err) {log("bad capper.config file" + err); throw(err);} - var config = JSON.parse(data); - port = config.port; - domain = config.protocol + "://" + config.domain + ":" + config.port + "/"; - log("config domain " + domain); - deferStart.resolve(true); -}); -var sslOptions = { - key: fs.readFileSync('./ssl/server.key'), - cert: fs.readFileSync('./ssl/server.crt'), - ca: fs.readFileSync('./ssl/ca.crt'), - requestCert: true, - rejectUnauthorized: false +var makeReviver = require("./saver").makeReviver; +var makeSaver = require("./saver").makeSaver; +var io = require("./saver"); + + +function main(argv, require, crypto, fs, path, createServer, express) { + const rd = p => io.fsReadAccess(fs, path.join, p); + const unique = caplib.makeUnique(crypto.randomBytes); + const dbfile = io.fsSyncAccess(fs, path.join, "capper.db"); + const reviver = makeReviver(require); + const saver = makeSaver(unique, dbfile, reviver.toMaker) + + makeConfig(rd("capper.config")).then(config => { + run(argv, config, reviver, saver, rd("./ssl"), + createServer, express); + }); +} + +var exports = module.exports; +exports.caplib = caplib; +exports.makeSaver = makeSaver; +exports.fsSyncAccess = io.fsSyncAccess; +exports.fsWriteAccess = io.fsWriteAccess; +exports.fsReadAccess = io.fsReadAccess; + +/*:: +type Config = { + domain: string, + port: number }; -var app = express(); +*/ +exports.makeConfig = makeConfig; +function makeConfig(rd /*: ReadAccess */) /*: Promise*/ { + return rd.readText("utf8").then(data => { + var config = JSON.parse(data); + var port = config.port; + var domain = config.protocol + "://" + config.domain + ":" + config.port + "/"; + console.log("config domain " + domain); + return {domain: domain, port: port}; + }).catch(err => {console.error("bad capper.config file", err); throw(new Error(err));}); +} + +function sslOptions(files /*: ReadAccess */) { + var file = name => files.subRd(name).readBytes(); + return Q.spread( + [file("server.key"), file("server.crt"), file("ca.crt")], + (key, cert, ca) => ({ + key: key, + cert: cert, + ca: ca, + requestCert: true, + rejectUnauthorized: false + })); +} + + function parseBody(req, res, next) { - req.rawBody = ''; - req.setEncoding('utf8'); + req.rawBody = ""; + req.setEncoding("utf8"); - req.on('data', function(chunk) { - req.rawBody += chunk; - }); + req.on("data", function(chunk) { + req.rawBody += chunk; + }); - req.on('end', function() { - try {req.body = JSON.parse(req.rawBody);} catch (e) {} - next(); - }); + req.on("end", function() { + try {req.body = JSON.parse(req.rawBody);} catch (e) { + // ignore JSON.parse errors + } + next(); + }); } -function reviverToUIPath(reviver) { - var revstring = "./apps/"; - var parts = reviver.split("."); - revstring = revstring + parts[0] + "/ui/"; - var filename = parts.length > 1 ? parts[1] : "index"; - revstring += filename + ".html"; - return revstring; -} -function getObj(req) { - var cred = req.query.s; - var id = saver.credToId(cred); - var reviver = saver.reviver(id); - var live = saver.live(id); + +exports.makeSturdy = makeSturdy; +function makeSturdy(saver /*: Saver*/, domain /*: string*/) { + + function vowAnsToVowJSONString(vowAns /*: Promise */) { + return vowAns.then(function(ans) { + var result = caplib.deepObjToJSON(ans, idToWebkey, saver); + console.log(result); + if (result !== null && typeof result === "object" && ("@" in result)) { + return JSON.stringify(result); + } else {return JSON.stringify({"=": result});} + }, function(err){ + console.error("vowAnsToVowJSONString err ", err); + return JSON.stringify({"!": err}); + }); + } + /** + * webkeyToLive(wkeyObj) looks to see if the arg is a webkeyObject, if so, + * returns a live ref, otherwise returns the arg unchanged + */ + function webkeyToLive(wkeyObj /*: any*/) { + try { + if (wkeyObj["@"]) { + var cred = wkeyObj["@"].split("#s=")[1]; + //console.log("wkeyObj is webkey, cred is " + cred); + return saver.live(saver.credToId(cred)); + } else {return wkeyObj;} + } catch (err) {return wkeyObj;} + } + function idToWebkey(id /*: Object*/) { + return domain + "ocaps/#s=" + saver.idToCred(id); + } + + function wkeyStringToLive(keyString /*: string*/) { + return webkeyToLive({"@": keyString}); + } + return { - cred: cred, - reviver: reviver, - live: live, - id: id, - method: req.query.q + idToWebkey: idToWebkey, + vowAnsToVowJSONString: vowAnsToVowJSONString, + webkeyToLive: webkeyToLive, + wkeyStringToLive: wkeyStringToLive }; } -/** -* webkeyToLive(wkeyObj) looks to see if the arg is a webkeyObject, if so, -* returns a live ref, otherwise returns the arg unchanged -*/ -function webkeyToLive(wkeyObj) { - try { - if (wkeyObj["@"]) { - var cred = wkeyObj["@"].split("#s=")[1]; - //log("wkeyObj is webkey, cred is " + cred); - return saver.live(saver.credToId(cred)); - } else {return wkeyObj;} - } catch (err) {return wkeyObj;} -} -function idToWebkey(id) { - return domain + "ocaps/#s=" + saver.idToCred(id); -} -app.get("/views/:filename", function(req, res) { - res.sendfile("./views/" + req.params.filename); -}); -app.get("/views/:path1/:filename", function(req, res) { - res.sendfile("./views/" + req.params.path1 + "/" + req.params.filename); -}); -app.get("/apps/:theapp/ui/:filename", function(req, res) { - res.sendfile("./apps/" + req.params.theapp + "/ui/" + req.params.filename); -}); -app.get('/', function(req, res) { - res.sendfile('./views/index.html'); -}); -function showActor(req, res) { - if (!req.query.s ) { - res.sendfile("./bootstrap.html"); - } else { - try { - var objdata = getObj(req); - if (!objdata.live) {res.send("no such object");} - //res.setHeader("Content-Security-Policy", "default-src: 'self'"); - //log("set CSP header"); - //res.writeHead(200) - res.sendfile(reviverToUIPath(objdata.reviver)); - } catch (err) { - res.send("Object not Found"); - //res.close(); + + +function makeApp(express, saver, sturdy, sendUI) { + var webkeyToLive = sturdy.webkeyToLive; + var vowAnsToVowJSONString = sturdy.vowAnsToVowJSONString; + + const view = path => __dirname + "/views/" + path; + var app = express(); + app.get("/views/*", function(req, res) { + res.sendfile(view(req.params[0])); + }); + app.get("/apps/:theapp/ui/*", function(req, res) { + sendUI(res, req.params.theapp, req.params[0]); + }); + app.get("/", function(req, res) { + res.sendfile(view("index.html")); + }); + + function getObj(req) { + var cred = req.query.s; + var id = saver.credToId(cred); + var reviver = saver.reviver(id); + var live = saver.live(id); + return { + cred: cred, + reviver: reviver, + live: live, + id: id, + method: req.query.q + }; + } + + function showActor(req, res) { + if (!req.query.s ) { + res.sendfile(__dirname + "/bootstrap.html"); + } else { + try { + var objdata = getObj(req); + if (!objdata.live) {res.send("no such object");} + //res.setHeader("Content-Security-Policy", "default-src: 'self'"); + //console.log("set CSP header"); + //res.writeHead(200) + sendUI(res, objdata.reviver); + } catch (err) { + console.error('showActor not found:', err); + res.send("Object not Found"); + //res.close(); + } } } -} -app.get("/ocaps/", showActor); - -function vowAnsToVowJSONString(vowAns) { - return vowAns.then(function(ans) { - var result = caplib.deepObjToJSON(ans, idToWebkey, saver); - log(JSON.stringify(result)) - if (result !== null && typeof result === "object" && ("@" in result)) { - return JSON.stringify(result); - } else {return JSON.stringify({"=": result});} - }, function(err){ - log("vowAnsToVowJSONString err " + err); - return JSON.stringify({"!": err}); - }); -} + app.get("/ocaps/", showActor); -function invokeActor(req, res){ - log("post query " + JSON.stringify(req.query)); - log("post body: " + JSON.stringify(req.body)); - var objdata = getObj(req); - if (!objdata.live) { - res.send(JSON.stringify({"!": "bad object invoked"})); - log("bad object invoked: " + JSON.stringify(req.query)); - return; + function invokeActor(req, res){ + console.log("post query ", req.query); + console.log("post body: ", req.body); + var objdata = getObj(req); + if (!objdata.live) { + res.send(JSON.stringify({"!": "bad object invoked"})); + console.error("bad object invoked: ", req.query); + return; + } + var args = req.body; //body already parsed + var translatedArgs = [objdata.id, objdata.method]; + args.forEach(function(next) {translatedArgs.push(webkeyToLive(next));}); + var vowAns = saver.deliver.apply(null, translatedArgs); + vowAnsToVowJSONString(vowAns).then(function(jsonString){ + console.log("returning: ", jsonString); + res.send(jsonString); + }).done(); } - var args = req.body; //body already parsed - var translatedArgs = [objdata.id, objdata.method]; - args.forEach(function(next) {translatedArgs.push(webkeyToLive(next));}); - var vowAns = saver.deliver.apply(null, translatedArgs); - vowAnsToVowJSONString(vowAns).then(function(jsonString){ - log("returning: " + jsonString); - res.send(jsonString); - }); -} -app.post("/ocaps/", parseBody, invokeActor); + app.post("/ocaps/", parseBody, invokeActor); -function wkeyStringToLive(keyString) { - return webkeyToLive({"@": keyString}); + return app; } -deferStart.promise.then(function(){ - var argMap = caplib.argMap(process.argv, wkeyStringToLive); + + +/*:: +// only import the types, not the ambient authority. +const https = require("https"); +const http = require("http"); + */ + +exports.run = run; +function run(argv /*: Array*/, + config /*: Config*/, + reviver /*: Reviver*/, + saver /*: Saver*/, + sslDir /*: ReadAccess*/, + createServer /*: typeof https.createServer */, + express /*: () => Application */) { + const sturdy = makeSturdy(saver, config.domain); + + var argMap = caplib.argMap(argv, sturdy.wkeyStringToLive); if ("-drop" in argMap) { saver.drop(saver.credToId(argMap["-drop"][0])); saver.checkpoint().then(function() {console.log("drop done");}); } else if ("-make" in argMap){ var obj = saver.make.apply(undefined, argMap["-make"]); - if (!obj) {log("cannot find maker " + argMap["-make"]); return;} + if (!obj) {console.error("cannot find maker " + argMap["-make"]); return;} saver.checkpoint().then(function() { - log(idToWebkey(saver.asId(obj))); - }); + console.log(sturdy.idToWebkey(saver.asId(obj))); + }).done(); } else if ("-post" in argMap) { var args = argMap["-post"]; if (typeof args[0] !== "object") { - log("bad target object webkey; forget '@'?"); + console.error("bad target object webkey; forget '@'?"); } else if (typeof args[1] !== "string") { - log("method to invoke is not a string"); + console.error("method to invoke is not a string"); } else { args[0] = saver.asId(args[0]); var vowAns = saver.deliver.apply(undefined, args); - vowAnsToVowJSONString(vowAns).then(function(answer){ - log(answer); + sturdy.vowAnsToVowJSONString(vowAns).then(function(answer){ + console.log(answer); }); } } else { - var s = https.createServer(sslOptions, app); - s.listen(port); + const app = makeApp(express, saver, sturdy, reviver.sendUI); + sslOptions(sslDir).then(sslOpts => { + var s = createServer(sslOpts, app); + s.listen(config.port); + }).done(); } -}); +} + +exports.runNaked = runNaked; +function runNaked(config /*: Config*/, + reviver /*: Reviver*/, + saver /*: Saver*/, + createServer /*: typeof http.createServer */, + express /*: () => Application */) { + const sturdy = makeSturdy(saver, config.domain); + const app = makeApp(express, saver, sturdy, reviver.sendUI); + var s = createServer(app); + s.listen(config.port); +} + + +if (require.main == module) { + main(process.argv, + require, // to load app modules + require("crypto"), + require("fs"), require("path"), + require("https").createServer, + require("express") + ); +} diff --git a/test/hello.js b/test/hello.js index 55a997b..582a8c8 100755 --- a/test/hello.js +++ b/test/hello.js @@ -1,13 +1,13 @@ /*global require, describe, it, console */ var assert = require("assert"); -var saver = require("../saver"); +var saver = require("../saver").ezSaver(require).saver; describe ("hello", function() { "use strict"; - it("setGetGreeting ", function() { + it("setGetGreeting ", function() { var hello = saver.make("hello"); hello.setGreeting("TestHello"); - assert(hello.greet() === "TestHello", + assert(hello.greet() === "TestHello", "Set Greeting matches Retrieved Greeting"); saver.drop(saver.asId(hello)); }); -}); \ No newline at end of file +}); diff --git a/test/liveKeyIdconversions.js b/test/liveKeyIdconversions.js index 945d51d..912de8a 100755 --- a/test/liveKeyIdconversions.js +++ b/test/liveKeyIdconversions.js @@ -1,6 +1,6 @@ /*global require describe it console */ var assert = require("assert"); -var saver = require("../saver"); +var saver = require("../saver").ezSaver(require).saver; var fs = require("fs"); var caplib = require("../caplib"); describe ("liveKeyIdconversions", function() { @@ -32,4 +32,4 @@ describe ("liveKeyIdconversions", function() { saver.drop(incrId); }); // var vowNewInc = saver.deliver(incrId, "incr", []); -}); \ No newline at end of file +}); diff --git a/test/personDelegation.js b/test/personDelegation.js index 14cf0b6..604cb7b 100755 --- a/test/personDelegation.js +++ b/test/personDelegation.js @@ -1,6 +1,6 @@ /*global require describe it console */ var assert = require("assert"); -var saver = require("../saver"); +var saver = require("../saver").ezSaver(require).saver; var fs = require("fs"); var Q = require("q"); describe ("MemberPersonDelegation", function() { @@ -90,4 +90,4 @@ describe ("MemberPersonDelegation", function() { admin.kill(); assert(saver.live(admDelId) === null, "share of killed root admin should not exist"); }); -}); \ No newline at end of file +}); diff --git a/test/sealer.js b/test/sealer.js index 4155050..c6a6cc4 100755 --- a/test/sealer.js +++ b/test/sealer.js @@ -1,9 +1,9 @@ /*global require describe it console */ "use strict"; var assert = require("assert"); -var saver = require("../saver"); +var saver = require("../saver").ezSaver(require).saver; describe ("sealer", function() { - it("seal&unseal ", function() { + it("seal&unseal ", function() { var shared = saver.make("shared"); var sealer = saver.make("sealer.makeSealer", shared); var unsealer = saver.make("sealer.makeUnsealer", shared); @@ -15,4 +15,4 @@ describe ("sealer", function() { saver.drop(saver.asId(sealer)); saver.drop(saver.asId(unsealer)); }); -}); \ No newline at end of file +}); diff --git a/test/stress.js b/test/stress.js index cf0747b..1ad1b2b 100755 --- a/test/stress.js +++ b/test/stress.js @@ -1,6 +1,6 @@ /*global require, describe, it, console */ var assert = require("assert"); -var saver = require("../saver"); +var saver = require("../saver").ezSaver(require).saver; var Q = require("q"); describe ("checkpoint sequentiality", function() { "use strict"; @@ -29,4 +29,4 @@ describe ("checkpoint sequentiality", function() { done(); }); }); -}); \ No newline at end of file +}); diff --git a/test/testcaplib.js b/test/testcaplib.js index 8ba8910..17bed35 100644 --- a/test/testcaplib.js +++ b/test/testcaplib.js @@ -1,12 +1,13 @@ var assert = require("assert"); var caplib = require("../caplib"); +var unique = caplib.makeUnique(require("crypto").randomBytes); describe ("caplib", function() { it("should return not equal for 3 and 4", function() { assert(3!==4); - }); + }); it("unique should return 25 char distinct tokens ", function() { - var t1 = caplib.unique(); - var t2 = caplib.unique(); + var t1 = unique(); + var t2 = unique(); assert(t1.length == 25, "not 25 char long"); assert(t2 !== t1, "not matching uniques"); }); @@ -34,4 +35,4 @@ describe ("caplib", function() { assert(map["-make"][0] === "money.makePurse" && map["-make"][1] === 3, "make is good"); }); -}); \ No newline at end of file +}); diff --git a/test/testsaver.js b/test/testsaver.js index a7cb6c6..763999e 100644 --- a/test/testsaver.js +++ b/test/testsaver.js @@ -1,6 +1,6 @@ /*global require describe it console */ var assert = require("assert"); -var saver = require("../saver"); +var saver = require("../saver").ezSaver(require).saver; var fs = require("fs"); var Q = require("q"); describe ("saver", function() { @@ -95,4 +95,4 @@ describe ("saver", function() { done("checkpoint promise rejected: " + err); }); }); -}); \ No newline at end of file +}); diff --git a/test/tpurse.js b/test/tpurse.js index 7ceb961..e726ec9 100644 --- a/test/tpurse.js +++ b/test/tpurse.js @@ -1,9 +1,9 @@ /*global require describe it console */ "use strict"; var assert = require("assert"); -var saver = require("../saver"); +var saver = require("../saver").ezSaver(require).saver; describe ("purse", function() { - it("withdraw&deposit ", function() { + it("withdraw&deposit ", function() { var shared = saver.make("shared"); var shareId = saver.asId(shared); var rootPurse = saver.make("money.makePurse", shared, 200000); @@ -14,11 +14,11 @@ describe ("purse", function() { assert(smallPurse.balance() === 30 && rootPurse.balance() === 200000-30, "post withdraw balances"); var amount = rootPurse.deposit(smallPurse); - assert(amount === 30 && rootPurse.balance() === 200000 && + assert(amount === 30 && rootPurse.balance() === 200000 && smallPurse.balance() ===0, "post deposit balances"); saver.drop(shareId); saver.drop(smallId); saver.drop(rootId); }); -}); \ No newline at end of file +});