From 6259083a3f6e929d5770b5868a3d20675a4c75cf Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 25 Feb 2016 21:07:43 -0600 Subject: [PATCH 01/34] copyFile: actually write the data that got read --- caplib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caplib.js b/caplib.js index f5cab22..a52e9c1 100644 --- a/caplib.js +++ b/caplib.js @@ -176,7 +176,7 @@ function argMap(argv, webkeyStringToLive) { function copyFile(src, dest) { var data = fs.readFileSync(src); - fs.writeFileSync(dest); + fs.writeFileSync(dest, data); } function copyRecurse(src, dest) { var exists = fs.existsSync(src); From cb23cf2f36469beb3e2a32b3dae02d1c11997032 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 25 Feb 2016 21:11:05 -0600 Subject: [PATCH 02/34] Proxy.set: return true to indicate success mismatch with es6 specs discovered using flow --- saver.js | 1 + 1 file changed, 1 insertion(+) diff --git a/saver.js b/saver.js index 04d97db..459b0f4 100644 --- a/saver.js +++ b/saver.js @@ -209,6 +209,7 @@ module.exports = function(){ } } } else {data[key] = val;} + return true; }, has: function(name) {return name in data;} //delete, iterate, keys From 87ab2022e7769a97e9f4f8928a91f65b8ff1eff7 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 25 Feb 2016 21:20:38 -0600 Subject: [PATCH 03/34] caplib: explict access to random, files rather than ambient --- caplib.js | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/caplib.js b/caplib.js index f5cab22..5af6264 100644 --- a/caplib.js +++ b/caplib.js @@ -17,9 +17,6 @@ information regarding how to obtain the source code for this library. /*global console, require, module */ module.exports = function() { "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 @@ -77,6 +74,8 @@ function tvalid(args, types, additionalTest, errMsg) { return valid(typeCheck(args, types) && additionalTest, errMsg); } +function makeUnique(crypto) { + function unique() { var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; var ans = ""; @@ -91,6 +90,12 @@ function unique() { //} return ans; } + + return { + unique: unique + }; +} + function makeSealerPair() { var holder = null; function seal(x) { @@ -174,6 +179,9 @@ function argMap(argv, webkeyStringToLive) { return args; } + +function makeFileUtils(fs, path) { + function copyFile(src, dest) { var data = fs.readFileSync(src); fs.writeFileSync(dest); @@ -205,17 +213,22 @@ function makeNewServer() { copyIfAbsent("ssl"); } + return { + makeNewServer: makeNewServer, + copyRecurse: copyRecurse + }; +} + var self = { typeCheck: typeCheck, - unique: unique, + makeUnique: makeUnique, valid: valid, tvalid: tvalid, makeSealerPair: makeSealerPair, cloneMap: cloneMap, argMap:argMap, deepObjToJSON: deepObjToJSON, - makeNewServer: makeNewServer, - copyRecurse: copyRecurse + makeFileUtils: makeFileUtils }; return self; From caa3c5497e4e4af22561acd9cef2292420ec44fd Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 25 Feb 2016 21:29:37 -0600 Subject: [PATCH 04/34] saver: explicit access to files, random, loading --- saver.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/saver.js b/saver.js index 04d97db..d83ea47 100644 --- a/saver.js +++ b/saver.js @@ -21,6 +21,9 @@ var caplib = require("./caplib"); module.exports = function(){ "use strict"; + + function makeSaver(unique, fs, fss, require) { + var dbfile = "capper.db"; function log(text) {console.log(text);} @@ -79,8 +82,8 @@ module.exports = function(){ **/ var sysState = {}; function loadSysState(){ - if (!fs.existsSync(dbfile)) {fs.writeFileSync(dbfile, "{}");} - var jsonState = fs.readFileSync(dbfile, 'utf8'); + if (!fss.existsSync(dbfile)) {fss.writeFileSync(dbfile, "{}");} + var jsonState = fss.readFileSync(dbfile, 'utf8'); if (jsonState.length === 0) {jsonState = "{}";} sysState = JSON.parse(jsonState); } @@ -250,7 +253,7 @@ module.exports = function(){ } 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); @@ -326,4 +329,7 @@ module.exports = function(){ drop: drop }; return Object.freeze(self); -}(); \ No newline at end of file +} + + return makeSaver; +}(); From fcaedb44979ab4a5126fc53006facdaa1fa0e2b8 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 25 Feb 2016 21:39:57 -0600 Subject: [PATCH 05/34] server: move getObj(), vowAnsToVowJSONString() Move them closer to other functions that need similar authority. --- server.js | 52 +++++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/server.js b/server.js index 582d9a5..30ce197 100644 --- a/server.js +++ b/server.js @@ -65,18 +65,18 @@ function reviverToUIPath(reviver) { 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); - return { - cred: cred, - reviver: reviver, - live: live, - id: id, - method: req.query.q - }; + +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}); + }); } /** * webkeyToLive(wkeyObj) looks to see if the arg is a webkeyObject, if so, @@ -106,6 +106,21 @@ app.get("/apps/:theapp/ui/:filename", function(req, res) { app.get('/', function(req, res) { res.sendfile('./views/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("./bootstrap.html"); @@ -125,19 +140,6 @@ function showActor(req, res) { } 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}); - }); -} - function invokeActor(req, res){ log("post query " + JSON.stringify(req.query)); log("post body: " + JSON.stringify(req.body)); From 08e5aac02c7f7eb3c99db4df2c38e506f068435d Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 25 Feb 2016 21:53:39 -0600 Subject: [PATCH 06/34] server: authority derives from CLI invocation - import powerful objects only if (require.main == module), i.e. only when this module is invoked from the command line - pass access to files, network, randomness explicitly rather than using ambient authority - to makeConfig(), sslOptions(), makeSturdy(), makeApp() --- server.js | 78 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/server.js b/server.js index 30ce197..00d59cf 100644 --- a/server.js +++ b/server.js @@ -16,14 +16,13 @@ information regarding how to obtain the source code for this library. /*global require, console, process */ "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 makeSaver = require("./saver"); var log = function(text) {console.log(text);}; +function makeConfig(fs) { + var domain; var port; var deferStart = Q.defer(); @@ -33,8 +32,13 @@ fs.readFile("capper.config", "utf8", function(err, data) { port = config.port; domain = config.protocol + "://" + config.domain + ":" + config.port + "/"; log("config domain " + domain); - deferStart.resolve(true); + deferStart.resolve({domain: domain, port: port}); }); + + return deferStart.promise; +} + +function sslOptions(fs) { var sslOptions = { key: fs.readFileSync('./ssl/server.key'), cert: fs.readFileSync('./ssl/server.crt'), @@ -42,7 +46,10 @@ var sslOptions = { requestCert: true, rejectUnauthorized: false }; -var app = express(); + return sslOptions; +} + + function parseBody(req, res, next) { req.rawBody = ''; req.setEncoding('utf8'); @@ -66,6 +73,8 @@ function reviverToUIPath(reviver) { return revstring; } +function makeSturdy(saver, domain) { + function vowAnsToVowJSONString(vowAns) { return vowAns.then(function(ans) { var result = caplib.deepObjToJSON(ans, idToWebkey, saver); @@ -94,6 +103,26 @@ function webkeyToLive(wkeyObj) { function idToWebkey(id) { return domain + "ocaps/#s=" + saver.idToCred(id); } + +function wkeyStringToLive(keyString) { + return webkeyToLive({"@": keyString}); +} + + return { + idToWebkey: idToWebkey, + vowAnsToVowJSONString: vowAnsToVowJSONString, + webkeyToLive: webkeyToLive, + wkeyStringToLive + }; +} + +// express is *nearly* powerless, but it seems to appeal +// to at least path.resolve() +function makeApp(express, saver, sturdy) { + var webkeyToLive = sturdy.webkeyToLive; + var vowAnsToVowJSONString = sturdy.vowAnsToVowJSONString; + +var app = express(); app.get("/views/:filename", function(req, res) { res.sendfile("./views/" + req.params.filename); }); @@ -160,11 +189,20 @@ function invokeActor(req, res){ } app.post("/ocaps/", parseBody, invokeActor); -function wkeyStringToLive(keyString) { - return webkeyToLive({"@": keyString}); + return app; } -deferStart.promise.then(function(){ - var argMap = caplib.argMap(process.argv, wkeyStringToLive); + + +function main(argv, require, crypto, fs, fsSync, https, express) { + const unique = caplib.makeUnique(crypto); + const saver = makeSaver(unique.unique, fs, fsSync, require); + + makeConfig(fs).then(config => { + const sturdy = makeSturdy(saver, config.domain); + const wkeyStringToLive = sturdy.wkeyStringToLive; + const idToWebkey = sturdy.idToWebkey; + + var argMap = caplib.argMap(argv, wkeyStringToLive); if ("-drop" in argMap) { saver.drop(saver.credToId(argMap["-drop"][0])); saver.checkpoint().then(function() {console.log("drop done");}); @@ -188,7 +226,25 @@ deferStart.promise.then(function(){ }); } } else { + const app = makeApp(express, saver, config.domain); + const sslOptions = sslOptions(fsSync); + const port = config.port; + var s = https.createServer(sslOptions, app); s.listen(port); } -}); + }); +} + + +if (require.main == module) { + main(process.argv, + require, // to load app modules + require("crypto"), + { readFile: require("fs").readFile, + writeFile: require("fs").writeFile }, + require("fs"), + { createServer: require("https").createServer }, + require("express") + ); +} From f768691afa1ec1729a3078d6876bd79db81c69d9 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 25 Feb 2016 23:46:35 -0600 Subject: [PATCH 07/34] oops... left-over require("fs") at top level of saver --- saver.js | 1 - 1 file changed, 1 deletion(-) diff --git a/saver.js b/saver.js index d83ea47..4ce8f0b 100644 --- a/saver.js +++ b/saver.js @@ -15,7 +15,6 @@ information regarding how to obtain the source code for this library. */ /*global require, Map, console, Proxy, setImmediate */ -var fs = require("fs"); var Q = require("q"); var caplib = require("./caplib"); From 7d59d72d7cd052ca1c6e38df618acc9b5aa18fbd Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 25 Feb 2016 23:53:52 -0600 Subject: [PATCH 08/34] oops: don't shadow sslOptions; bind vowAnsToVowJSONString --- server.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index 00d59cf..abc2626 100644 --- a/server.js +++ b/server.js @@ -201,6 +201,7 @@ function main(argv, require, crypto, fs, fsSync, https, express) { const sturdy = makeSturdy(saver, config.domain); const wkeyStringToLive = sturdy.wkeyStringToLive; const idToWebkey = sturdy.idToWebkey; + const vowAnsToVowJSONString = sturdy.vowAnsToVowJSONString; var argMap = caplib.argMap(argv, wkeyStringToLive); if ("-drop" in argMap) { @@ -227,10 +228,10 @@ function main(argv, require, crypto, fs, fsSync, https, express) { } } else { const app = makeApp(express, saver, config.domain); - const sslOptions = sslOptions(fsSync); + const sslOpts = sslOptions(fsSync); const port = config.port; - var s = https.createServer(sslOptions, app); + var s = https.createServer(sslOpts, app); s.listen(port); } }); From 34aeed72adc2ab57eb2c482e73324d47d7937e72 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Fri, 26 Feb 2016 00:10:16 -0600 Subject: [PATCH 09/34] annotations and minor tweaks for static typechecking - .flowconfig, jshint declarations - annotations for exports from caplib, saver - name interface types in interfaces/types.js - borrow es6 Proxy declaration from typescript - adapt misc. node.js API types from typescript - tweak commandArgs loop in argMap to not mutate strings to numbers in-place - tweak copyRecurse isDirectory test to avoid nulls - tweak asId to make statically evident that it can't return null - use single assignment for newContext initialization - explicitly check target[method] for null since flow doesn't appreciate that it's surrounded by try/catch --- .flowconfig | 2 ++ caplib.js | 39 +++++++++++++++++++-------------- interfaces/es6.js | 34 +++++++++++++++++++++++++++++ interfaces/node.js | 53 +++++++++++++++++++++++++++++++++++++++++++++ interfaces/types.js | 30 +++++++++++++++++++++++++ saver.js | 26 ++++++++++++++-------- server.js | 3 +++ 7 files changed, 162 insertions(+), 25 deletions(-) create mode 100644 .flowconfig create mode 100644 interfaces/es6.js create mode 100644 interfaces/node.js create mode 100644 interfaces/types.js diff --git a/.flowconfig b/.flowconfig new file mode 100644 index 0000000..61c6f57 --- /dev/null +++ b/.flowconfig @@ -0,0 +1,2 @@ +[libs] +interfaces/ diff --git a/caplib.js b/caplib.js index ed7435b..1839b72 100644 --- a/caplib.js +++ b/caplib.js @@ -12,9 +12,12 @@ 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 */ +/* jshint esversion: 6, node: true */ module.exports = function() { "use strict"; /** @@ -42,7 +45,7 @@ module.exports = function() { * 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", @@ -64,17 +67,17 @@ function typeCheck(args, validTypes) { 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 makeUnique(crypto) { +function makeUnique(crypto /*: Crypto*/) /*: Unique*/{ function unique() { var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; @@ -96,7 +99,7 @@ function unique() { }; } -function makeSealerPair() { +function makeSealerPair() /*: SealerPair*/{ var holder = null; function seal(x) { var box = function() {holder = x;}; @@ -112,7 +115,7 @@ 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; @@ -131,7 +134,9 @@ function cloneMap(m) { * 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") { @@ -144,7 +149,7 @@ function deepObjToJSON(obj, idToWebkey, saver) { 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); } @@ -159,7 +164,8 @@ function deepObjToJSON(obj, idToWebkey, saver) { * Converts args preceded with "#" to numbers * Converts args preceded with "@" to live refs * */ -function argMap(argv, webkeyStringToLive) { +function argMap(argv /*: Array*/, + webkeyStringToLive /*: (webkey: string) => any */) /*: Object*/{ var args = {}; var serverIndex; for (serverIndex = 0; @@ -167,12 +173,14 @@ function argMap(argv, webkeyStringToLive) { serverIndex++) {} if (serverIndex === argv.length - 1) {return args;} var command = argv[serverIndex+1]; - var commandArgs = argv.splice(serverIndex+2); - commandArgs.forEach(function(next, i) { + var commandArgs /*: Array*/ = []; + argv.splice(serverIndex+2).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)); + } else { + commandArgs[i] = next; } }); args[command] = commandArgs; @@ -180,16 +188,15 @@ function argMap(argv, webkeyStringToLive) { } -function makeFileUtils(fs, path) { +function makeFileUtils(fs /*:FSSync*/, path /*: Path*/) /*: FileUtils*/ { function copyFile(src, dest) { var data = fs.readFileSync(src); fs.writeFileSync(dest, data); } -function copyRecurse(src, dest) { +function copyRecurse(src /*: string*/, dest /*: string*/) /*: void*/{ var exists = fs.existsSync(src); - var stats = exists && fs.statSync(src); - var isDirectory = exists && stats.isDirectory(); + var isDirectory = exists ? fs.statSync(src).isDirectory() : null; if (exists && isDirectory) { fs.mkdirSync(dest); fs.readdirSync(src).forEach(function(childItemName) { @@ -232,4 +239,4 @@ var self = { }; return self; -}(); \ No newline at end of file +}(); diff --git a/interfaces/es6.js b/interfaces/es6.js new file mode 100644 index 0000000..56c280d --- /dev/null +++ b/interfaces/es6.js @@ -0,0 +1,34 @@ +// cribbed from +// https://github.com/Microsoft/TypeScript/blob/master/src/lib/es6.d.ts +// +// Proxy.create seems to be part of harmon proxies, +// which "is superseded by the newer "direct proxies API." +// http://wiki.ecmascript.org/doku.php?id=harmony:proxies + +type PropertyKey = string | number | Symbol; + +type ProxyHandler = { + getPrototypeOf? : (target: T) => any; + setPrototypeOf? : (target: T, v: any) => boolean; + isExtensible? : (target: T) => boolean; + preventExtensions? : (target: T) => boolean; + // getOwnPropertyDescriptor? : (target: T, p: PropertyKey) => PropertyDescriptor; + has? : (target: T, p: PropertyKey) => boolean; + get? : (target: T, p: PropertyKey, receiver: any) => any; + set? : (target: T, p: PropertyKey, value: any, receiver: any) => boolean; + deleteProperty? : (target: T, p: PropertyKey) => boolean; + // defineProperty? : (target: T, p: PropertyKey, attributes: PropertyDescriptor) => boolean; + enumerate? : (target: T) => PropertyKey[]; + ownKeys? : (target: T) => PropertyKey[]; + apply? : (target: T, thisArg: any, argArray?: any) => any; + construct? : (target: T, thisArg: any, argArray?: any) => any; +} + +type ProxyConstructor = { + revocable(target: T, handler: ProxyHandler): { proxy: T; revoke: () => void; }; + new (target: T, handler: ProxyHandler): T; + + create (handler: ProxyHandler): T +} +declare var Proxy: ProxyConstructor; + diff --git a/interfaces/node.js b/interfaces/node.js new file mode 100644 index 0000000..c1a1ce9 --- /dev/null +++ b/interfaces/node.js @@ -0,0 +1,53 @@ +// cribbed from +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/node/node.d.ts +type FSAccess = { + writeFile(filename: string, data: any, callback?: (err: ErrnoException) => void): void; +} +type FSSync = { + existsSync(path: string): boolean; + readFileSync(filename: string, options?: T): U; + writeFileSync(filename: string, data: string | Buffer, options?: { encoding?: string; mode?: number; flag?: string; }): 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; + +type Crypto = { + randomBytes(qty: number): Array +} + diff --git a/interfaces/types.js b/interfaces/types.js new file mode 100644 index 0000000..5f8b3c2 --- /dev/null +++ b/interfaces/types.js @@ -0,0 +1,30 @@ +type Saver = { + deliver(id: Object, method: string, ...optArgs: Array): Promise; + make(makerLocation: string, optInitArgs: ?Array): Object; + reviver(id: Object): string; + hasId(ref: any): bool; + asId(ref: any): Object; + idToCred(id: Object): string; + credToId(cred: string): Object; + live(id: Object): any; + checkpoint(): Promise, + drop(id: Object): void; +}; + +type Loader = (path: string) => any; + +type FileUtils = { + makeNewServer(): void; + copyRecurse(src : string, dest : string): void; +} + + +type SealerPair = { + seal(x: T): any; + unseal(boxy: any): T +} + +type Unique = { + unique(): string +} + diff --git a/saver.js b/saver.js index 611dd84..eb6afbe 100644 --- a/saver.js +++ b/saver.js @@ -12,16 +12,21 @@ 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 */ +/* jshint esversion: 6, node: true */ var Q = require("q"); var caplib = require("./caplib"); module.exports = function(){ "use strict"; - function makeSaver(unique, fs, fss, require) { + function makeSaver(unique /*: () => string*/, + fs /*: FSAccess*/, fss /*: FSSync*/, + require /*: Loader*/) /*: Saver */ { var dbfile = "capper.db"; function log(text) {console.log(text);} @@ -130,11 +135,12 @@ module.exports = function(){ var liveState = {}; //each key is the ref to the live object, the value is the id that contains the cred - var liveToId = new Map(); + var liveToId /*: Map*/ = new Map(); - function asId(ref) { - if (liveToId.has(ref)){return liveToId.get(ref); } - throw ("asId bad ref"); + 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);} @@ -235,11 +241,12 @@ module.exports = function(){ * */ function makeContext(id, constructorLocation) { - var newContext = {make: make, state: new State(id)}; - newContext.drop = function() {drop(id);}; - newContext.isPersistent = function(obj) { + 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); } @@ -288,7 +295,8 @@ module.exports = function(){ var ansPair = Q.defer(); var target = live(id); try { - var ans = target[method].apply(undefined, actualArgs); + var targetMethod = target ? target[method] : {}; + var ans = targetMethod.apply(undefined, actualArgs); checkpoint().then(function(ok){ ansPair.resolve(ans); }); diff --git a/server.js b/server.js index abc2626..dd23b13 100644 --- a/server.js +++ b/server.js @@ -12,9 +12,12 @@ 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 */ +/* jshint esversion: 6, node: true */ "use strict"; var Q = require("q"); var caplib = require("./caplib"); From 755c3ccf884293796ec4c9acfbe6aed19674aac8 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sun, 28 Feb 2016 22:47:05 -0600 Subject: [PATCH 10/34] update indentation to match ocap scope changes While we're at it, get tool support for consistent style from eslint: - indent 4 spaces. no tabs. no trailing space - one true brace style with the single-line exception - except for makeSaver, where emacs got confused - double quotes for strings - for unused args, use _x name style - prune rawSend dead code relax some of eslint's defaults: - console logging is OK --- package.json | 39 +++++++ saver.js | 241 ++++++++++++++++++++------------------- server.js | 315 ++++++++++++++++++++++++++------------------------- 3 files changed, 317 insertions(+), 278 deletions(-) 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 eb6afbe..b80a780 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 @@ -17,43 +17,42 @@ information regarding how to obtain the source code for this library. */ /*global require, Map, console, Proxy, setImmediate */ -/* jshint esversion: 6, node: true */ +/* eslint-env node */ +"use strict"; var Q = require("q"); var caplib = require("./caplib"); -module.exports = function(){ - "use strict"; - - function makeSaver(unique /*: () => string*/, - fs /*: FSAccess*/, fss /*: FSSync*/, - require /*: Loader*/) /*: Saver */ { - +exports.makeSaver = makeSaver; +function makeSaver(unique /*: () => string*/, + fs /*: FSAccess*/, fss /*: FSSync*/, + require /*: Loader*/) /*: Saver */ +{ var dbfile = "capper.db"; 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, @@ -62,49 +61,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(){ + + /** + * 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 (!fss.existsSync(dbfile)) {fss.writeFileSync(dbfile, "{}");} - var jsonState = fss.readFileSync(dbfile, 'utf8'); + var jsonState = fss.readFileSync(dbfile, "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) { @@ -114,29 +115,29 @@ 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); + console.log("checkpoint failed: " + err); + vowPair.reject(err); } else {vowPair.resolve(true);} //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 /*: Map*/ = new Map(); - + function asId(ref) /*: Object*/{ var id = liveToId.get(ref); if (id === undefined) throw ("asId bad ref"); @@ -147,27 +148,27 @@ module.exports = function(){ 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) { @@ -193,41 +194,41 @@ module.exports = function(){ 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;} //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 @@ -238,19 +239,21 @@ 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), - drop: function() {drop(id);}, - 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); - } - + return Object.freeze(newContext); + } + function reviverToMaker(reviver) { var parts = reviver.split("."); var path = "./apps/" + parts[0] +"/server/main.js"; @@ -258,86 +261,82 @@ module.exports = function(){ if (parts.length === 2) {maker = maker[parts[1]];} return maker; } - make = function(makerLocation, optInitArgs) { + make = function(makerLocation, _optInitArgs) { setupCheckpoint(); 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 { const sturdy = makeSturdy(saver, config.domain); @@ -206,37 +207,37 @@ function main(argv, require, crypto, fs, fsSync, https, express) { const idToWebkey = sturdy.idToWebkey; const vowAnsToVowJSONString = sturdy.vowAnsToVowJSONString; - var argMap = caplib.argMap(argv, 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;} - saver.checkpoint().then(function() { - log(idToWebkey(saver.asId(obj))); - }); - } else if ("-post" in argMap) { - var args = argMap["-post"]; - if (typeof args[0] !== "object") { - log("bad target object webkey; forget '@'?"); - } else if (typeof args[1] !== "string") { - log("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); + var argMap = caplib.argMap(argv, 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;} + saver.checkpoint().then(function() { + log(idToWebkey(saver.asId(obj))); }); - } - } else { - const app = makeApp(express, saver, config.domain); - const sslOpts = sslOptions(fsSync); - const port = config.port; + } else if ("-post" in argMap) { + var args = argMap["-post"]; + if (typeof args[0] !== "object") { + log("bad target object webkey; forget '@'?"); + } else if (typeof args[1] !== "string") { + log("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); + }); + } + } else { + const app = makeApp(express, saver, config.domain); + const sslOpts = sslOptions(fsSync); + const port = config.port; - var s = https.createServer(sslOpts, app); - s.listen(port); - } + var s = https.createServer(sslOpts, app); + s.listen(port); + } }); } From 7d51296be35fa6e444eaedda9b7808f96461fe14 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 3 Mar 2016 20:41:21 -0600 Subject: [PATCH 11/34] oops... makeApp() takes a sturdy, not a domain --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index dbdef93..9ded1af 100644 --- a/server.js +++ b/server.js @@ -231,7 +231,7 @@ function main(argv, require, crypto, fs, fsSync, https, express) { }); } } else { - const app = makeApp(express, saver, config.domain); + const app = makeApp(express, saver, sturdy); const sslOpts = sslOptions(fsSync); const port = config.port; From e00e401bab25bc7d9d0098d84891cf8c665769c5 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 3 Mar 2016 20:42:13 -0600 Subject: [PATCH 12/34] makeSaver only needs reviverToMaker, not require --- saver.js | 9 +-------- server.js | 10 +++++++++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/saver.js b/saver.js index b80a780..6986994 100644 --- a/saver.js +++ b/saver.js @@ -25,7 +25,7 @@ var caplib = require("./caplib"); exports.makeSaver = makeSaver; function makeSaver(unique /*: () => string*/, fs /*: FSAccess*/, fss /*: FSSync*/, - require /*: Loader*/) /*: Saver */ + reviverToMaker /*: (path: string) => any*/) /*: Saver */ { var dbfile = "capper.db"; function log(text) {console.log(text);} @@ -254,13 +254,6 @@ function makeSaver(unique /*: () => string*/, 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; - } make = function(makerLocation, _optInitArgs) { setupCheckpoint(); var cred = unique(); diff --git a/server.js b/server.js index 9ded1af..9093e42 100644 --- a/server.js +++ b/server.js @@ -199,7 +199,7 @@ function makeApp(express, saver, sturdy) { function main(argv, require, crypto, fs, fsSync, https, express) { var unique = caplib.makeUnique(crypto); - var saver = makeSaver(unique.unique, fs, fsSync, require); + var saver = makeSaver(unique.unique, fs, fsSync, reviverToMaker); makeConfig(fs).then(config => { const sturdy = makeSturdy(saver, config.domain); @@ -239,6 +239,14 @@ function main(argv, require, crypto, fs, fsSync, https, express) { s.listen(port); } }); + + 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; + } } From ae0e16ba335d39264960bb0aee8a50feb02ab71a Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 3 Mar 2016 22:31:59 -0600 Subject: [PATCH 13/34] caplib.js: delete trailing whitespace --- caplib.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/caplib.js b/caplib.js index 1839b72..eb304eb 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 @@ -22,7 +22,7 @@ module.exports = function() { "use strict"; /** * 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,25 +41,25 @@ module.exports = function() { * 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 /*:Array*/, validTypes /*: string*/) /*:bool*/ { +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 + + console.log("typeCheck bad arg " + i + " should be: " + typ + " was " + typeof(args[i])); return false; } @@ -122,13 +122,13 @@ function cloneMap(m /*: Object*/) /*: Object*/{ } /** * 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. @@ -147,7 +147,7 @@ function deepObjToJSON(obj /*:any*/, if (saver.hasId(obj)) { var webkey = idToWebkey(saver.asId(obj)); return {"@" : webkey}; - } + } if (saver.live(obj)) {return {"@": idToWebkey(obj)};} var clone /*: any*/= obj instanceof Array ? [] : {}; for (var key in obj) { @@ -157,19 +157,19 @@ function deepObjToJSON(obj /*:any*/, } /** - * 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 /*: Array*/, webkeyStringToLive /*: (webkey: string) => any */) /*: Object*/{ - var args = {}; + var args = {}; var serverIndex; - for (serverIndex = 0; - serverIndex < argv.length && argv[serverIndex].indexOf ("server") < 0; + for (serverIndex = 0; + serverIndex < argv.length && argv[serverIndex].indexOf ("server") < 0; serverIndex++) {} if (serverIndex === argv.length - 1) {return args;} var command = argv[serverIndex+1]; From ccc59127e22fecef1be5ef00dc07e581d2b71092 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 3 Mar 2016 22:36:33 -0600 Subject: [PATCH 14/34] caplib.js: normalize whitespace; prune makeFileUtils dead code --- caplib.js | 91 +++++++++++++++++-------------------------------------- 1 file changed, 28 insertions(+), 63 deletions(-) diff --git a/caplib.js b/caplib.js index eb304eb..fa8aa9b 100644 --- a/caplib.js +++ b/caplib.js @@ -16,9 +16,9 @@ information regarding how to obtain the source code for this library. @flow */ -/*global console, require, module */ +/*global console, module */ /* jshint esversion: 6, node: true */ -module.exports = function() { + "use strict"; /** * Function typeCheck allows a quick, concise limited form of duck typing of the @@ -60,7 +60,7 @@ function typeCheck(args /*:Array*/, validTypes /*: string*/) /*:bool*/ { var typ = validTypes[i]; if (typeof(args[i]) !== types[typ]) { console.log("typeCheck bad arg " + i + " should be: " + typ + - " was " + typeof(args[i])); + " was " + typeof(args[i])); return false; } } @@ -79,20 +79,20 @@ function tvalid(args /*:Array*/, types /*: string*/, additionalTest /*:bool function makeUnique(crypto /*: Crypto*/) /*: Unique*/{ -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 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]; + } + //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: unique @@ -136,7 +136,8 @@ function cloneMap(m /*: Object*/) /*: Object*/{ * */ function deepObjToJSON(obj /*:any*/, idToWebkey /*: (id: Object) => string*/, - saver /*: Saver*/) /*: any */ { + saver /*: Saver*/) /*: any */ +{ if (obj === undefined) {return null;} if (obj === null) {return null;} if (typeof obj === "function") { @@ -156,6 +157,7 @@ function deepObjToJSON(obj /*:any*/, return clone; } + /** * 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 @@ -165,16 +167,19 @@ function deepObjToJSON(obj /*:any*/, * Converts args preceded with "@" to live refs * */ function argMap(argv /*: Array*/, - webkeyStringToLive /*: (webkey: string) => any */) /*: Object*/{ + webkeyStringToLive /*: (webkey: string) => any */) /*: Object*/ +{ var args = {}; var serverIndex; for (serverIndex = 0; - serverIndex < argv.length && argv[serverIndex].indexOf ("server") < 0; - serverIndex++) {} + serverIndex < argv.length && argv[serverIndex].indexOf ("server") < 0; + serverIndex++) { + // next arg... + } if (serverIndex === argv.length - 1) {return args;} var command = argv[serverIndex+1]; var commandArgs /*: Array*/ = []; - argv.splice(serverIndex+2).forEach(function(next, i) { + argv.splice(serverIndex+2).forEach(function(next, i) { if (next.indexOf("#") === 0) { commandArgs[i] = parseFloat(next.substring(1)); } else if (next.indexOf("@") === 0) { @@ -188,44 +193,6 @@ function argMap(argv /*: Array*/, } -function makeFileUtils(fs /*:FSSync*/, path /*: Path*/) /*: FileUtils*/ { - -function copyFile(src, dest) { - var data = fs.readFileSync(src); - fs.writeFileSync(dest, data); -} -function copyRecurse(src /*: string*/, dest /*: string*/) /*: void*/{ - var exists = fs.existsSync(src); - var isDirectory = exists ? fs.statSync(src).isDirectory() : null; - 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); - } - } - copyIfAbsent("capper.db"); - copyIfAbsent("capper.config"); - copyIfAbsent("apps"); - copyIfAbsent("test"); - copyIfAbsent("views"); - copyIfAbsent("ssl"); -} - - return { - makeNewServer: makeNewServer, - copyRecurse: copyRecurse - }; -} - var self = { typeCheck: typeCheck, makeUnique: makeUnique, @@ -234,9 +201,7 @@ var self = { makeSealerPair: makeSealerPair, cloneMap: cloneMap, argMap:argMap, - deepObjToJSON: deepObjToJSON, - makeFileUtils: makeFileUtils + deepObjToJSON: deepObjToJSON }; -return self; -}(); +module.exports = self; From 65cbdb558dbcb3ae06a8844cf2716ddbe09c31bd Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 3 Mar 2016 23:55:02 -0600 Subject: [PATCH 15/34] avoid strange typechecking errors in .json files --- .flowconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.flowconfig b/.flowconfig index 61c6f57..f66e76e 100644 --- a/.flowconfig +++ b/.flowconfig @@ -1,2 +1,7 @@ [libs] interfaces/ + +[ignore] +.*node_modules/.*/test/.*\.json +.*node_modules/.*/tables/.*\.json +.*node_modules/resolve/lib/.*\.json From afa5465a9115551110812e9bd39ef53c9d0c3a83 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Thu, 3 Mar 2016 23:56:08 -0600 Subject: [PATCH 16/34] toward type declarations for express --- interfaces/express.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 interfaces/express.js 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; +} + From 7ef485a16d91d3ef86011ffd8b3d5ac40e5acb22 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Fri, 4 Mar 2016 00:10:47 -0600 Subject: [PATCH 17/34] Capper organized as library: complete draft - reduce filesystem access given to makeSaver - ReadAccess, WriteAccess, SyncAccess give access to a specific path - just one FileSystem type from node.js - server.js: factor run() out of main() - use ReadAccess for capper.config, ./ssl/* - no need for readFileSync in sslOptions - refactor reviverToMaker, reviverToUIPath as as Reviver.toMaker and Reviver.sendUI - sendUI takes an express Response, which carries authority to open files by path name - never mind note about express being nearly powerless - log errors in showActor --- interfaces/node.js | 12 ++- interfaces/types.js | 27 ++++- saver.js | 16 +-- server.js | 234 ++++++++++++++++++++++++++++---------------- 4 files changed, 190 insertions(+), 99 deletions(-) diff --git a/interfaces/node.js b/interfaces/node.js index c1a1ce9..1cb5bdf 100644 --- a/interfaces/node.js +++ b/interfaces/node.js @@ -1,12 +1,16 @@ // cribbed from // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/node/node.d.ts -type FSAccess = { - writeFile(filename: string, data: any, callback?: (err: ErrnoException) => void): void; -} -type FSSync = { +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; diff --git a/interfaces/types.js b/interfaces/types.js index 5f8b3c2..15c8a73 100644 --- a/interfaces/types.js +++ b/interfaces/types.js @@ -1,3 +1,8 @@ +type Reviver = { + toMaker(reviver: string): any, + sendUI(res: Response, reviver: string): void; +}; + type Saver = { deliver(id: Object, method: string, ...optArgs: Array): Promise; make(makerLocation: string, optInitArgs: ?Array): Object; @@ -11,11 +16,25 @@ type Saver = { drop(id: Object): void; }; -type Loader = (path: string) => any; -type FileUtils = { - makeNewServer(): void; - copyRecurse(src : string, dest : string): 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; } diff --git a/saver.js b/saver.js index 6986994..9175ec6 100644 --- a/saver.js +++ b/saver.js @@ -24,10 +24,9 @@ var caplib = require("./caplib"); exports.makeSaver = makeSaver; function makeSaver(unique /*: () => string*/, - fs /*: FSAccess*/, fss /*: FSSync*/, + dbfile /*: SyncAccess*/, reviverToMaker /*: (path: string) => any*/) /*: Saver */ { - var dbfile = "capper.db"; function log(text) {console.log(text);} // modifiedObjs has a cred as key and null as value; it is a set @@ -85,8 +84,8 @@ function makeSaver(unique /*: () => string*/, **/ var sysState = {}; function loadSysState() { - if (!fss.existsSync(dbfile)) {fss.writeFileSync(dbfile, "{}");} - var jsonState = fss.readFileSync(dbfile, "utf8"); + if (!dbfile.existsSync()) {dbfile.writeSync("{}");} + var jsonState = dbfile.readTextSync("utf8"); if (jsonState.length === 0) {jsonState = "{}";} sysState = JSON.parse(jsonState); } @@ -118,13 +117,14 @@ function makeSaver(unique /*: () => string*/, 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) { + var dbunsync = dbfile.unsync(); + return dbunsync.writeText(jsonState) + .then(function() {vowPair.resolve(true)}) + .catch(function (err) { console.log("checkpoint failed: " + err); vowPair.reject(err); - } else {vowPair.resolve(true);} + }); //log("db size after write: "+ fs.statSync("capper.db").size); - }); }); } return vowPair.promise; diff --git a/server.js b/server.js index 9093e42..7a81a2b 100644 --- a/server.js +++ b/server.js @@ -24,31 +24,86 @@ var caplib = require("./caplib"); var makeSaver = require("./saver").makeSaver; var log = function(text) {console.log(text);}; -function makeConfig(fs) { - 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({domain: domain, port: port}); + +function main(argv, require, crypto, fs, path, createServer, express) { + var unique = caplib.makeUnique(crypto).unique; + var reviver = makeReviver(require); + const dbfile = fsSyncAccess(fs, path.join, "capper.db"); + var rd = p => fsReadAccess(fs, path.join, p); + var sslDir = rd("./ssl"); + + makeConfig(rd("capper.config")).then(config => { + run(argv, config, reviver, unique, sslDir, dbfile, + createServer, express); + }); +} + +var exports = module.exports; +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)) }); +} - return deferStart.promise; +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) + }); } -function sslOptions(fs) { - var sslOptions = { - key: fs.readFileSync("./ssl/server.key"), - cert: fs.readFileSync("./ssl/server.crt"), - ca: fs.readFileSync("./ssl/ca.crt"), - requestCert: true, - rejectUnauthorized: false - }; - return sslOptions; +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) + }); +} + +/*:: +type Config = { + domain: string, + port: number +}; +*/ +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 + "/"; + log("config domain " + domain); + return {domain: domain, port: port}; + }).catch(err => {log("bad capper.config file" + err); throw(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 + })); } @@ -68,15 +123,33 @@ function parseBody(req, res, 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 makeReviver(require) /*: Reviver*/ { + function toMaker(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; + } + + function sendUI(res, reviver) { + var revstring = "./apps/"; + 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 + }); } + + function makeSturdy(saver, domain) { function vowAnsToVowJSONString(vowAns) { @@ -120,9 +193,8 @@ function makeSturdy(saver, domain) { }; } -// express is *nearly* powerless, but it seems to appeal -// to at least path.resolve() -function makeApp(express, saver, sturdy) { + +function makeApp(express, saver, sturdy, sendUI) { var webkeyToLive = sturdy.webkeyToLive; var vowAnsToVowJSONString = sturdy.vowAnsToVowJSONString; @@ -164,8 +236,9 @@ function makeApp(express, saver, sturdy) { //res.setHeader("Content-Security-Policy", "default-src: 'self'"); //log("set CSP header"); //res.writeHead(200) - res.sendfile(reviverToUIPath(objdata.reviver)); + sendUI(res, objdata.reviver); } catch (err) { + console.log('showActor not found:', err); res.send("Object not Found"); //res.close(); } @@ -197,55 +270,52 @@ function makeApp(express, saver, sturdy) { } -function main(argv, require, crypto, fs, fsSync, https, express) { - var unique = caplib.makeUnique(crypto); - var saver = makeSaver(unique.unique, fs, fsSync, reviverToMaker); - - makeConfig(fs).then(config => { - const sturdy = makeSturdy(saver, config.domain); - const wkeyStringToLive = sturdy.wkeyStringToLive; - const idToWebkey = sturdy.idToWebkey; - const vowAnsToVowJSONString = sturdy.vowAnsToVowJSONString; - - var argMap = caplib.argMap(argv, 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;} - saver.checkpoint().then(function() { - log(idToWebkey(saver.asId(obj))); - }); - } else if ("-post" in argMap) { - var args = argMap["-post"]; - if (typeof args[0] !== "object") { - log("bad target object webkey; forget '@'?"); - } else if (typeof args[1] !== "string") { - log("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); - }); - } - } else { - const app = makeApp(express, saver, sturdy); - const sslOpts = sslOptions(fsSync); - const port = config.port; +/*:: +// only import the types, not the ambient authority. +const https = require("https"); + */ - var s = https.createServer(sslOpts, app); - s.listen(port); - } - }); +exports.run = run; +function run(argv /*: Array*/, + config /*: Config*/, + reviver /*: Reviver*/, + unique /*: () => string*/, + sslDir /*: ReadAccess*/, + dbfile /*: SyncAccess*/, + createServer /*: typeof https.createServer */, + express /*: () => Application */) { + const saver = makeSaver(unique, dbfile, reviver.toMaker); + const sturdy = makeSturdy(saver, config.domain); - 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; + 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;} + saver.checkpoint().then(function() { + log(sturdy.idToWebkey(saver.asId(obj))); + }); + } else if ("-post" in argMap) { + var args = argMap["-post"]; + if (typeof args[0] !== "object") { + log("bad target object webkey; forget '@'?"); + } else if (typeof args[1] !== "string") { + log("method to invoke is not a string"); + } else { + args[0] = saver.asId(args[0]); + var vowAns = saver.deliver.apply(undefined, args); + sturdy.vowAnsToVowJSONString(vowAns).then(function(answer){ + log(answer); + }); + } + } else { + const app = makeApp(express, saver, sturdy, reviver.sendUI); + sslOptions(sslDir).then(sslOpts => { + var s = createServer(sslOpts, app); + s.listen(config.port); + }); } } @@ -254,10 +324,8 @@ if (require.main == module) { main(process.argv, require, // to load app modules require("crypto"), - { readFile: require("fs").readFile, - writeFile: require("fs").writeFile }, - require("fs"), - { createServer: require("https").createServer }, + require("fs"), require("path"), + require("https").createServer, require("express") ); } From b3fe94fb264c7a935b175195fee94de38b4b441f Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 09:20:12 -0600 Subject: [PATCH 18/34] do not version control the .db --- capper.db | 1 - 1 file changed, 1 deletion(-) delete mode 100644 capper.db 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 From 0dc283637226df22851c8ebd1973d961808134f6 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 09:23:44 -0600 Subject: [PATCH 19/34] use separate names for version-controlled and edited config --- .gitignore | 2 ++ README.md | 2 +- capper.config => capper.config.example | 0 3 files changed, 3 insertions(+), 1 deletion(-) rename capper.config => capper.config.example (100%) 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/capper.config b/capper.config.example similarity index 100% rename from capper.config rename to capper.config.example From 892cddebfc2e1b7546d2f46da7571f5a0b835e11 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 14:40:20 -0600 Subject: [PATCH 20/34] refactor makeUnique(crypto).unique to makeUnique(randomBytes) --- caplib.js | 9 ++++----- interfaces/node.js | 5 ----- interfaces/types.js | 5 ----- server.js | 2 +- 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/caplib.js b/caplib.js index fa8aa9b..583b9c0 100644 --- a/caplib.js +++ b/caplib.js @@ -77,12 +77,13 @@ function tvalid(args /*:Array*/, types /*: string*/, additionalTest /*:bool return valid(typeCheck(args, types) && additionalTest, errMsg); } -function makeUnique(crypto /*: Crypto*/) /*: Unique*/{ +function makeUnique(randomBytes /*: (qty: number) => Array*/ + ) /*: () => string*/{ function unique() { var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; var ans = ""; - var buf = crypto.randomBytes(25); + var buf = randomBytes(25); for (var i = 0; i < buf.length; i++) { var index = buf[i] % chars.length; ans += chars[index]; @@ -94,9 +95,7 @@ function makeUnique(crypto /*: Crypto*/) /*: Unique*/{ return ans; } - return { - unique: unique - }; + return unique; } function makeSealerPair() /*: SealerPair*/{ diff --git a/interfaces/node.js b/interfaces/node.js index 1cb5bdf..e282ad4 100644 --- a/interfaces/node.js +++ b/interfaces/node.js @@ -50,8 +50,3 @@ type Stats = { } type ErrnoException = any; - -type Crypto = { - randomBytes(qty: number): Array -} - diff --git a/interfaces/types.js b/interfaces/types.js index 15c8a73..488892a 100644 --- a/interfaces/types.js +++ b/interfaces/types.js @@ -42,8 +42,3 @@ type SealerPair = { seal(x: T): any; unseal(boxy: any): T } - -type Unique = { - unique(): string -} - diff --git a/server.js b/server.js index 7a81a2b..d7c4509 100644 --- a/server.js +++ b/server.js @@ -26,7 +26,7 @@ var log = function(text) {console.log(text);}; function main(argv, require, crypto, fs, path, createServer, express) { - var unique = caplib.makeUnique(crypto).unique; + var unique = caplib.makeUnique(crypto.randomBytes); var reviver = makeReviver(require); const dbfile = fsSyncAccess(fs, path.join, "capper.db"); var rd = p => fsReadAccess(fs, path.join, p); From ba50b8e2324b3754e0ee46383377a4ad4620b561 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 14:41:20 -0600 Subject: [PATCH 21/34] export caplib from Capper --- server.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server.js b/server.js index d7c4509..d0cbda1 100644 --- a/server.js +++ b/server.js @@ -39,6 +39,8 @@ function main(argv, require, crypto, fs, path, createServer, express) { } var exports = module.exports; +exports.caplib = caplib; + exports.fsReadAccess = fsReadAccess; function fsReadAccess(fs /*: FileSystem */, join /*:(...parts: Array) => string*/, From 271d79be866cba73cf4ae8bfea02e5f7e4ad4a63 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 14:41:53 -0600 Subject: [PATCH 22/34] missing ssl/server.key was a silent failure use .done() to raise exceptions --- server.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server.js b/server.js index d0cbda1..ea3934a 100644 --- a/server.js +++ b/server.js @@ -264,7 +264,7 @@ function makeApp(express, saver, sturdy, sendUI) { vowAnsToVowJSONString(vowAns).then(function(jsonString){ log("returning: " + jsonString); res.send(jsonString); - }); + }).done(); } app.post("/ocaps/", parseBody, invokeActor); @@ -298,7 +298,7 @@ function run(argv /*: Array*/, if (!obj) {log("cannot find maker " + argMap["-make"]); return;} saver.checkpoint().then(function() { log(sturdy.idToWebkey(saver.asId(obj))); - }); + }).done(); } else if ("-post" in argMap) { var args = argMap["-post"]; if (typeof args[0] !== "object") { @@ -317,7 +317,7 @@ function run(argv /*: Array*/, sslOptions(sslDir).then(sslOpts => { var s = createServer(sslOpts, app); s.listen(config.port); - }); + }).done(); } } From ce763079cc3f49641adea6bdaba3eff8caa1461c Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 14:43:25 -0600 Subject: [PATCH 23/34] use sendUI() to send all the app's files not just the main page --- server.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/server.js b/server.js index ea3934a..6ebf6fc 100644 --- a/server.js +++ b/server.js @@ -16,7 +16,7 @@ information regarding how to obtain the source code for this library. @flow */ -/*global require, module, console, process */ +/*global require, module, console, process, __dirname */ /* eslint-env es6 */ "use strict"; var Q = require("q"); @@ -135,12 +135,16 @@ function makeReviver(require) /*: Reviver*/ { return maker; } - function sendUI(res, reviver) { + function sendUI(res, reviver, path /*: ?string*/) { var revstring = "./apps/"; - var parts = reviver.split("."); - revstring = revstring + parts[0] + "/ui/"; - var filename = parts.length > 1 ? parts[1] : "index"; - revstring += filename + ".html"; + 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); } @@ -200,18 +204,19 @@ 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/:filename", function(req, res) { - res.sendfile("./views/" + req.params.filename); + res.sendfile(view(req.params.filename)); }); app.get("/views/:path1/:filename", function(req, res) { - res.sendfile("./views/" + req.params.path1 + "/" + req.params.filename); + res.sendfile(view(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); + sendUI(res, req.params.theapp, req.params.filename); }); app.get("/", function(req, res) { - res.sendfile("./views/index.html"); + res.sendfile(view("index.html")); }); function getObj(req) { From 931e18ad72ab4aadcbbdbc48148f0706af2cd4e8 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 19:30:47 -0600 Subject: [PATCH 24/34] delete-trailing-whitespace; ensure newlines at EOF --- apps/sealer/server/main.js | 14 +++++++------- test/hello.js | 6 +++--- test/liveKeyIdconversions.js | 2 +- test/personDelegation.js | 2 +- test/sealer.js | 4 ++-- test/stress.js | 2 +- test/testcaplib.js | 4 ++-- test/testsaver.js | 2 +- test/tpurse.js | 6 +++--- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/sealer/server/main.js b/apps/sealer/server/main.js index 5405d75..933b83a 100755 --- a/apps/sealer/server/main.js +++ b/apps/sealer/server/main.js @@ -16,33 +16,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/test/hello.js b/test/hello.js index 55a997b..1118c95 100755 --- a/test/hello.js +++ b/test/hello.js @@ -3,11 +3,11 @@ var assert = require("assert"); var saver = 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..8221f4e 100755 --- a/test/liveKeyIdconversions.js +++ b/test/liveKeyIdconversions.js @@ -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..2c70e6e 100755 --- a/test/personDelegation.js +++ b/test/personDelegation.js @@ -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..043e67b 100755 --- a/test/sealer.js +++ b/test/sealer.js @@ -3,7 +3,7 @@ var assert = require("assert"); var saver = 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..95fd594 100755 --- a/test/stress.js +++ b/test/stress.js @@ -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..4a4dd37 100644 --- a/test/testcaplib.js +++ b/test/testcaplib.js @@ -3,7 +3,7 @@ var caplib = require("../caplib"); 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(); @@ -34,4 +34,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..76403c0 100644 --- a/test/testsaver.js +++ b/test/testsaver.js @@ -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..e05dff4 100644 --- a/test/tpurse.js +++ b/test/tpurse.js @@ -3,7 +3,7 @@ var assert = require("assert"); var saver = 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 +}); From 3d102acfe3060c4e205034e53d1300e81f37a697 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 19:32:54 -0600 Subject: [PATCH 25/34] consolidate saver dependencies - let ezSaver() convenience routine require anything - use __dirname in makeReviver() to isolate from caller's directory - move io builders (fsReadAccess ,...) to saver.js - pass saver to run() rather than dbfile --- saver.js | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- server.js | 82 ++++------------------------------------------------- 2 files changed, 89 insertions(+), 78 deletions(-) diff --git a/saver.js b/saver.js index 9175ec6..6663f2b 100644 --- a/saver.js +++ b/saver.js @@ -16,8 +16,8 @@ information regarding how to obtain the source code for this library. @flow */ -/*global require, Map, console, Proxy, setImmediate */ -/* eslint-env node */ +/*global require, Map, console, Proxy, setImmediate, __dirname */ +/* eslint-env es6, node */ "use strict"; var Q = require("q"); var caplib = require("./caplib"); @@ -333,3 +333,84 @@ function makeSaver(unique /*: () => string*/, }; return Object.freeze(self); } + + +exports.makeReviver = makeReviver; +function makeReviver(require /*: (mod: string) => 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 6ebf6fc..3fcc0ad 100644 --- a/server.js +++ b/server.js @@ -21,19 +21,18 @@ information regarding how to obtain the source code for this library. "use strict"; var Q = require("q"); var caplib = require("./caplib"); -var makeSaver = require("./saver").makeSaver; +var ezSaver = require("./saver").ezSaver; +var io = require("./saver"); var log = function(text) {console.log(text);}; function main(argv, require, crypto, fs, path, createServer, express) { - var unique = caplib.makeUnique(crypto.randomBytes); - var reviver = makeReviver(require); - const dbfile = fsSyncAccess(fs, path.join, "capper.db"); - var rd = p => fsReadAccess(fs, path.join, p); + var sr = ezSaver(require); + var rd = p => io.fsReadAccess(fs, path.join, p); var sslDir = rd("./ssl"); makeConfig(rd("capper.config")).then(config => { - run(argv, config, reviver, unique, sslDir, dbfile, + run(argv, config, sr.reviver, sr.saver, sslDir, createServer, express); }); } @@ -41,43 +40,6 @@ function main(argv, require, crypto, fs, path, createServer, express) { var exports = module.exports; exports.caplib = caplib; -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) - }); -} - /*:: type Config = { domain: string, @@ -126,36 +88,6 @@ function parseBody(req, res, next) { } -function makeReviver(require) /*: Reviver*/ { - function toMaker(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; - } - - 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 - }); -} - - - function makeSturdy(saver, domain) { function vowAnsToVowJSONString(vowAns) { @@ -286,12 +218,10 @@ exports.run = run; function run(argv /*: Array*/, config /*: Config*/, reviver /*: Reviver*/, - unique /*: () => string*/, + saver /*: Saver*/, sslDir /*: ReadAccess*/, - dbfile /*: SyncAccess*/, createServer /*: typeof https.createServer */, express /*: () => Application */) { - const saver = makeSaver(unique, dbfile, reviver.toMaker); const sturdy = makeSturdy(saver, config.domain); var argMap = caplib.argMap(argv, sturdy.wkeyStringToLive); From e1cc2da2ba10eb78c03bfb8e9693e99f89a629fb Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 19:38:48 -0600 Subject: [PATCH 26/34] update tests, sealer app w.r.t. ocap discipline - pass crypto.randomBytes explicitly to makeUnique - use ezSaver convenience routine in tests --- apps/sealer/server/main.js | 1 + test/hello.js | 2 +- test/liveKeyIdconversions.js | 2 +- test/personDelegation.js | 2 +- test/sealer.js | 2 +- test/stress.js | 2 +- test/testcaplib.js | 5 +++-- test/testsaver.js | 2 +- test/tpurse.js | 2 +- 9 files changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/sealer/server/main.js b/apps/sealer/server/main.js index 933b83a..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({ diff --git a/test/hello.js b/test/hello.js index 1118c95..582a8c8 100755 --- a/test/hello.js +++ b/test/hello.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; describe ("hello", function() { "use strict"; it("setGetGreeting ", function() { diff --git a/test/liveKeyIdconversions.js b/test/liveKeyIdconversions.js index 8221f4e..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() { diff --git a/test/personDelegation.js b/test/personDelegation.js index 2c70e6e..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() { diff --git a/test/sealer.js b/test/sealer.js index 043e67b..c6a6cc4 100755 --- a/test/sealer.js +++ b/test/sealer.js @@ -1,7 +1,7 @@ /*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() { var shared = saver.make("shared"); diff --git a/test/stress.js b/test/stress.js index 95fd594..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"; diff --git a/test/testcaplib.js b/test/testcaplib.js index 4a4dd37..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"); }); diff --git a/test/testsaver.js b/test/testsaver.js index 76403c0..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() { diff --git a/test/tpurse.js b/test/tpurse.js index e05dff4..e726ec9 100644 --- a/test/tpurse.js +++ b/test/tpurse.js @@ -1,7 +1,7 @@ /*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() { var shared = saver.make("shared"); From 48288eb068aa8e4c0afd750a55e7c9912b796f93 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 19:48:41 -0600 Subject: [PATCH 27/34] don't use overly powerful ezSaver in server main() --- server.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/server.js b/server.js index 3fcc0ad..b9bf99b 100644 --- a/server.js +++ b/server.js @@ -21,18 +21,21 @@ information regarding how to obtain the source code for this library. "use strict"; var Q = require("q"); var caplib = require("./caplib"); -var ezSaver = require("./saver").ezSaver; +var makeReviver = require("./saver").makeReviver; +var makeSaver = require("./saver").makeSaver; var io = require("./saver"); var log = function(text) {console.log(text);}; function main(argv, require, crypto, fs, path, createServer, express) { - var sr = ezSaver(require); - var rd = p => io.fsReadAccess(fs, path.join, p); - var sslDir = rd("./ssl"); + 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, sr.reviver, sr.saver, sslDir, + run(argv, config, reviver, saver, rd("./ssl"), createServer, express); }); } From c502d7e985a944d292066558697accc09c78e368 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 19:55:15 -0600 Subject: [PATCH 28/34] export makeSaver and io routines --- server.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server.js b/server.js index b9bf99b..d500c76 100644 --- a/server.js +++ b/server.js @@ -42,6 +42,10 @@ function main(argv, require, crypto, fs, path, 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 = { From 09509a482fea362030c4bbef817ec48f7d15a1df Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 20:00:25 -0600 Subject: [PATCH 29/34] bootstrap.html ref was sensitive to cwd --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index d500c76..d67d223 100644 --- a/server.js +++ b/server.js @@ -174,7 +174,7 @@ function makeApp(express, saver, sturdy, sendUI) { function showActor(req, res) { if (!req.query.s ) { - res.sendfile("./bootstrap.html"); + res.sendfile(__dirname + "/bootstrap.html"); } else { try { var objdata = getObj(req); From 103f8bdb9e6f297b74cc151a50d69851386f0dc9 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 5 Mar 2016 21:46:30 -0600 Subject: [PATCH 30/34] route paths with multiple segments consolidate routes using /* and req.params[0] --- server.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/server.js b/server.js index d67d223..6fb0f95 100644 --- a/server.js +++ b/server.js @@ -145,14 +145,11 @@ function makeApp(express, saver, sturdy, sendUI) { const view = path => __dirname + "/views/" + path; var app = express(); - app.get("/views/:filename", function(req, res) { - res.sendfile(view(req.params.filename)); + app.get("/views/*", function(req, res) { + res.sendfile(view(req.params[0])); }); - app.get("/views/:path1/:filename", function(req, res) { - res.sendfile(view(req.params.path1 + "/" + req.params.filename)); - }); - app.get("/apps/:theapp/ui/:filename", function(req, res) { - sendUI(res, req.params.theapp, req.params.filename); + 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")); From c6c8fc46119d3f35e2e01346cdeec004bd90dd5b Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Fri, 9 Dec 2016 23:15:11 -0600 Subject: [PATCH 31/34] add runNaked() for use in sandstorm --- server.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/server.js b/server.js index 6fb0f95..ef459fe 100644 --- a/server.js +++ b/server.js @@ -216,6 +216,7 @@ function makeApp(express, saver, sturdy, sendUI) { /*:: // only import the types, not the ambient authority. const https = require("https"); +const http = require("http"); */ exports.run = run; @@ -260,6 +261,18 @@ function run(argv /*: Array*/, } } +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, From 234bd60061f1d7a22bdd1e3d48b688147b854209 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Fri, 9 Dec 2016 23:15:33 -0600 Subject: [PATCH 32/34] Proxy.create() obsolete in favor of new Proxy() in flow: Jul 18, 2016: 84a1a24@zacharygolba Add Proxy to core declarations somewhere before node 7.1, the --harmony-proxies option went away; https://github.com/tvcutsem/harmony-reflect says node groks as of 6.0. --- interfaces/es6.js | 34 ---------------------------------- saver.js | 2 +- 2 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 interfaces/es6.js diff --git a/interfaces/es6.js b/interfaces/es6.js deleted file mode 100644 index 56c280d..0000000 --- a/interfaces/es6.js +++ /dev/null @@ -1,34 +0,0 @@ -// cribbed from -// https://github.com/Microsoft/TypeScript/blob/master/src/lib/es6.d.ts -// -// Proxy.create seems to be part of harmon proxies, -// which "is superseded by the newer "direct proxies API." -// http://wiki.ecmascript.org/doku.php?id=harmony:proxies - -type PropertyKey = string | number | Symbol; - -type ProxyHandler = { - getPrototypeOf? : (target: T) => any; - setPrototypeOf? : (target: T, v: any) => boolean; - isExtensible? : (target: T) => boolean; - preventExtensions? : (target: T) => boolean; - // getOwnPropertyDescriptor? : (target: T, p: PropertyKey) => PropertyDescriptor; - has? : (target: T, p: PropertyKey) => boolean; - get? : (target: T, p: PropertyKey, receiver: any) => any; - set? : (target: T, p: PropertyKey, value: any, receiver: any) => boolean; - deleteProperty? : (target: T, p: PropertyKey) => boolean; - // defineProperty? : (target: T, p: PropertyKey, attributes: PropertyDescriptor) => boolean; - enumerate? : (target: T) => PropertyKey[]; - ownKeys? : (target: T) => PropertyKey[]; - apply? : (target: T, thisArg: any, argArray?: any) => any; - construct? : (target: T, thisArg: any, argArray?: any) => any; -} - -type ProxyConstructor = { - revocable(target: T, handler: ProxyHandler): { proxy: T; revoke: () => void; }; - new (target: T, handler: ProxyHandler): T; - - create (handler: ProxyHandler): T -} -declare var Proxy: ProxyConstructor; - diff --git a/saver.js b/saver.js index 6663f2b..ae1545b 100644 --- a/saver.js +++ b/saver.js @@ -188,7 +188,7 @@ function makeSaver(unique /*: () => string*/, } 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]); From bf677c5ff7f5bf296ad0d2910c3cffe7d500fa7c Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sat, 10 Dec 2016 21:46:21 -0600 Subject: [PATCH 33/34] log objects, not just strings goal is to log Error traces --- server.js | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/server.js b/server.js index ef459fe..1bcd992 100644 --- a/server.js +++ b/server.js @@ -24,7 +24,6 @@ var caplib = require("./caplib"); var makeReviver = require("./saver").makeReviver; var makeSaver = require("./saver").makeSaver; var io = require("./saver"); -var log = function(text) {console.log(text);}; function main(argv, require, crypto, fs, path, createServer, express) { @@ -59,9 +58,9 @@ function makeConfig(rd /*: ReadAccess */) /*: Promise*/ { var config = JSON.parse(data); var port = config.port; var domain = config.protocol + "://" + config.domain + ":" + config.port + "/"; - log("config domain " + domain); + console.log("config domain " + domain); return {domain: domain, port: port}; - }).catch(err => {log("bad capper.config file" + err); throw(err);}); + }).catch(err => {console.error("bad capper.config file", err); throw(new Error(err));}); } function sslOptions(files /*: ReadAccess */) { @@ -100,12 +99,12 @@ function makeSturdy(saver, domain) { function vowAnsToVowJSONString(vowAns) { return vowAns.then(function(ans) { var result = caplib.deepObjToJSON(ans, idToWebkey, saver); - log(JSON.stringify(result)); + console.log(result); if (result !== null && typeof result === "object" && ("@" in result)) { return JSON.stringify(result); } else {return JSON.stringify({"=": result});} }, function(err){ - log("vowAnsToVowJSONString err " + err); + console.error("vowAnsToVowJSONString err ", err); return JSON.stringify({"!": err}); }); } @@ -117,7 +116,7 @@ function makeSturdy(saver, domain) { try { if (wkeyObj["@"]) { var cred = wkeyObj["@"].split("#s=")[1]; - //log("wkeyObj is webkey, cred is " + cred); + //console.log("wkeyObj is webkey, cred is " + cred); return saver.live(saver.credToId(cred)); } else {return wkeyObj;} } catch (err) {return wkeyObj;} @@ -177,11 +176,11 @@ function makeApp(express, saver, sturdy, sendUI) { var objdata = getObj(req); if (!objdata.live) {res.send("no such object");} //res.setHeader("Content-Security-Policy", "default-src: 'self'"); - //log("set CSP header"); + //console.log("set CSP header"); //res.writeHead(200) sendUI(res, objdata.reviver); } catch (err) { - console.log('showActor not found:', err); + console.error('showActor not found:', err); res.send("Object not Found"); //res.close(); } @@ -190,12 +189,12 @@ function makeApp(express, saver, sturdy, sendUI) { app.get("/ocaps/", showActor); function invokeActor(req, res){ - log("post query " + JSON.stringify(req.query)); - log("post body: " + JSON.stringify(req.body)); + 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"})); - log("bad object invoked: " + JSON.stringify(req.query)); + console.error("bad object invoked: ", req.query); return; } var args = req.body; //body already parsed @@ -203,7 +202,7 @@ function makeApp(express, saver, sturdy, sendUI) { args.forEach(function(next) {translatedArgs.push(webkeyToLive(next));}); var vowAns = saver.deliver.apply(null, translatedArgs); vowAnsToVowJSONString(vowAns).then(function(jsonString){ - log("returning: " + jsonString); + console.log("returning: ", jsonString); res.send(jsonString); }).done(); } @@ -235,21 +234,21 @@ function run(argv /*: Array*/, 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(sturdy.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); sturdy.vowAnsToVowJSONString(vowAns).then(function(answer){ - log(answer); + console.log(answer); }); } } else { From 02498347930f2a3a5b82d1a7f5725c6e90db1dbe Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Wed, 27 Sep 2017 00:43:02 -0500 Subject: [PATCH 34/34] caplib: export makeParseArg - tighten up static types a bit --- caplib.js | 25 +++++++++++++++++-------- interfaces/types.js | 14 ++++++++------ saver.js | 2 +- server.js | 11 ++++++----- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/caplib.js b/caplib.js index 583b9c0..acf752c 100644 --- a/caplib.js +++ b/caplib.js @@ -176,21 +176,29 @@ function argMap(argv /*: Array*/, // next arg... } if (serverIndex === argv.length - 1) {return args;} + var parseArg = makeParseArg(webkeyStringToLive); var command = argv[serverIndex+1]; var commandArgs /*: Array*/ = []; argv.splice(serverIndex+2).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)); - } else { - commandArgs[i] = next; - } + commandArgs[i] = parseArg(next); }); args[command] = commandArgs; return args; } +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; + } +} + var self = { typeCheck: typeCheck, @@ -199,7 +207,8 @@ var self = { tvalid: tvalid, makeSealerPair: makeSealerPair, cloneMap: cloneMap, - argMap:argMap, + argMap: argMap, + makeParseArg: makeParseArg, deepObjToJSON: deepObjToJSON }; diff --git a/interfaces/types.js b/interfaces/types.js index 488892a..185caf2 100644 --- a/interfaces/types.js +++ b/interfaces/types.js @@ -3,17 +3,19 @@ type Reviver = { sendUI(res: Response, reviver: string): void; }; +type Id = {id: string} + type Saver = { - deliver(id: Object, method: string, ...optArgs: Array): Promise; + deliver(id: Id, method: string, ...optArgs: Array): Promise; make(makerLocation: string, optInitArgs: ?Array): Object; - reviver(id: Object): string; + reviver(id: Id): string; hasId(ref: any): bool; asId(ref: any): Object; - idToCred(id: Object): string; - credToId(cred: string): Object; - live(id: Object): any; + idToCred(id: Id): string; + credToId(cred: string): Id; + live(id: Id): any; checkpoint(): Promise, - drop(id: Object): void; + drop(id: Id): void; }; diff --git a/saver.js b/saver.js index ae1545b..1d8b874 100644 --- a/saver.js +++ b/saver.js @@ -220,7 +220,7 @@ function makeSaver(unique /*: () => string*/, } 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; diff --git a/server.js b/server.js index 1bcd992..32a389a 100644 --- a/server.js +++ b/server.js @@ -94,9 +94,10 @@ function parseBody(req, res, next) { } -function makeSturdy(saver, domain) { +exports.makeSturdy = makeSturdy; +function makeSturdy(saver /*: Saver*/, domain /*: string*/) { - function vowAnsToVowJSONString(vowAns) { + function vowAnsToVowJSONString(vowAns /*: Promise */) { return vowAns.then(function(ans) { var result = caplib.deepObjToJSON(ans, idToWebkey, saver); console.log(result); @@ -112,7 +113,7 @@ function makeSturdy(saver, domain) { * 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) { + function webkeyToLive(wkeyObj /*: any*/) { try { if (wkeyObj["@"]) { var cred = wkeyObj["@"].split("#s=")[1]; @@ -121,11 +122,11 @@ function makeSturdy(saver, domain) { } else {return wkeyObj;} } catch (err) {return wkeyObj;} } - function idToWebkey(id) { + function idToWebkey(id /*: Object*/) { return domain + "ocaps/#s=" + saver.idToCred(id); } - function wkeyStringToLive(keyString) { + function wkeyStringToLive(keyString /*: string*/) { return webkeyToLive({"@": keyString}); }