<commit_before>var Pull = require("pull-stream");
var Ansuz = require("ansuz");
var Query = require("./query");
var Dump = require("./dump");
var Net = require("net");
var Pad = require("pad-ipv6");

var Server = module.exports = {};

var log = {
    req: function (req, opt) {
        if (!(opt && opt.verbose)) { return; }
        var q = req.question[0];
        console.log();
        console.log({
            name: q.name,
            type: q.type,
            class: q.class,
        });
    },
};

function CachingResolver(sbot) {
    this.sbot = sbot;
    this.cache = {/* name+class+type: result */};
    this.cbs = {/* name+class+type: [callback] */};
}

CachingResolver.prototype.answer = function (req, res, opt) {
    log.req(req, opt);

    // one query per dns message
    var q = req.question[0];

    // TODO validate queries more carefully
    if (!q) {
        console.error("invalid question");
        res.responseCode = 1; // FORMERR
        res.end();
        return;
    }

    this.query(q, function (err, result) {
        if (err) {
            console.error(err.stack || err);
            res.responseCode = 2; // SERVFAIL
            return res.end();
        }
        if (result.authoritative) {
            res.authoritative = true;

            if (!result.domainExists) {
                res.responseCode = 3; // NXDOMAIN
            }
        }
        if (opt && opt.verbose) {
            var recs = records.map(Dump.recordToLine).join(", ")
            var auths = authorities.map(Dump.recordToLine).join(", ")
            console.log("%s: %s%s", q.name, recs,
                auths ? '. auths: ' : '', auths);
        }
        res.answer = result.answers;
        res.authority = result.authorities;
        res.end();
    });
};

CachingResolver.prototype.query = function (q, cb) {
    var key = q.name + ' ' + q.class + ' ' + q.type;
    var result = this.cache[key];
    if (result) {
        // cache hit
        if (result.expires > Date.now()) {
            return cb(null, result);
        } else {
            // expired
            delete this.cache[key];
        }
    }

    var cbs = this.cbs[key];
    if (cbs) return cbs.push(cb);
    cbs = this.cbs[key] = [cb];
    var self = this;

    Query.query(this.sbot, q, function (err, result) {
        if (!err) self.cache[key] = result;
        while (cbs.length) cbs.shift()(err, result);
        delete self.cbs[key];
    });
};

var createServer = Server.create = function (sbot, port, host, cb, opt) {
    if (Net.isIPv6(host)) { host = Pad(host); }

    var resolver = new CachingResolver(sbot);

    var Dnsd = require("modern-dnsd");
    var server = Dnsd.createServer(function(req, res) {
        resolver.answer(req, res, opt);
    });

    server.on('error', function (msg, error, conn) {
        console.error(error.stack)
    });

    return server.listen(port, host, cb);
};

Server.listen = function (sbot, port, host, cb, opt) {
    var server = createServer(sbot, port, host, cb, opt);

    var close = function () {
        console.error("Server connection lost. Shutting down");
        sbot.close();
        server.close();
    };

    sbot.on('closed', close);
};

<commit_msg>Periodically purge expired results from cache<commit_after>var Pull = require("pull-stream");
var Ansuz = require("ansuz");
var Query = require("./query");
var Dump = require("./dump");
var Net = require("net");
var Pad = require("pad-ipv6");

var Server = module.exports = {};

var log = {
    req: function (req, opt) {
        if (!(opt && opt.verbose)) { return; }
        var q = req.question[0];
        console.log();
        console.log({
            name: q.name,
            type: q.type,
            class: q.class,
        });
    },
};

function queryKey(q) {
    return q.name + ' ' + qrec.class || 'IN') + ' ' + rec.type;
}

function CachingResolver(sbot) {
    this.sbot = sbot;
    this.cache = {/* name+class+type: result */};
    this.cbs = {/* name+class+type: [callback] */};
}

CachingResolver.prototype.answer = function (req, res, opt) {
    log.req(req, opt);

    // one query per dns message
    var q = req.question[0];

    // TODO validate queries more carefully
    if (!q) {
        console.error("invalid question");
        res.responseCode = 1; // FORMERR
        res.end();
        return;
    }

    this.query(q, function (err, result) {
        if (err) {
            console.error(err.stack || err);
            res.responseCode = 2; // SERVFAIL
            return res.end();
        }
        if (result.authoritative) {
            res.authoritative = true;

            if (!result.domainExists) {
                res.responseCode = 3; // NXDOMAIN
            }
        }
        if (opt && opt.verbose) {
            var recs = records.map(Dump.recordToLine).join(", ")
            var auths = authorities.map(Dump.recordToLine).join(", ")
            console.log("%s: %s%s", q.name, recs,
                auths ? '. auths: ' : '', auths);
        }
        res.answer = result.answers;
        res.authority = result.authorities;
        res.end();
    });
};

CachingResolver.prototype.query = function (q, cb) {
    var key = queryKey(q);
    var result = this.cache[key];
    if (result) {
        // cache hit
        if (result.expires > Date.now()) {
            return cb(null, result);
        } else {
            // expired
            delete this.cache[key];
        }
    }

    var cbs = this.cbs[key];
    if (cbs) return cbs.push(cb);
    cbs = this.cbs[key] = [cb];
    var self = this;

    Query.query(this.sbot, q, function (err, result) {
        if (!err) self.cache[key] = result;
        while (cbs.length) cbs.shift()(err, result);
        delete self.cbs[key];
    });
};

CachingResolver.prototype.autoPurge = function (interval) {
    return setInterval(this.purgeExpired.bind(this), interval);
};

CachingResolver.prototype.purgeExpired = function () {
    var now = Date.now();
    for (var key in cache) {
        var result = this.cache[key];
        if (now > result.expires) {
            delete cache[key];
        }
    }
};

var createServer = Server.create = function (sbot, port, host, cb, opt) {
    if (Net.isIPv6(host)) { host = Pad(host); }

    var resolver = new CachingResolver(sbot);
    resolver.autoPurge(180e3);

    var Dnsd = require("modern-dnsd");
    var server = Dnsd.createServer(function(req, res) {
        resolver.answer(req, res, opt);
    });

    server.on('error', function (msg, error, conn) {
        console.error(error.stack)
    });

    return server.listen(port, host, cb);
};

Server.listen = function (sbot, port, host, cb, opt) {
    var server = createServer(sbot, port, host, cb, opt);

    var close = function () {
        console.error("Server connection lost. Shutting down");
        sbot.close();
        server.close();
    };

    sbot.on('closed', close);
};

<|endoftext|>


<commit_before>"use strict";

/**
 * Main server.
 * Handles a very simple kind of dependency injection, to be refactored to some service
 * registry ting.
 *
 */

    var mod_connect         = require('connect')
    , mod_sys               = require('sys')
    , mod_path              = require('path')
    , mod_resourceservice   = null
    , mod_resourcemanager   = null
    , mod_tagmanager        = require('./tagmanager.js')
    , mod_cache             = require('./cache.js')
    , mod_socketio          = require('./socketio.js')
    , mod_modules           = null
    , mod_fs                = require('fs')
    , cache                 = null
    , redisclient           = null
    , logger                = require('./logger.js').getLogger(mod_path.basename(module.filename))

if (process.argv.length < 3) {
    logger.error('usage: server <configfile>');
    process.exit();
}

// [TBD] handle arguments properly
// [TBD] dynamic config service (requires dependency management first)
var config = null;
logger.info('reading config from ' + process.argv[2]);
mod_fs.readFile(process.argv[2], function (err, data) {
    if (err) {
        logger.error('error reading configuration');
        process.exit();
    }
    config = JSON.parse(data);
    logger.info('config loaded');
    mod_cache.configure(config);
    mod_resourcemanager = require('./resourcemanager.js')(config, mod_cache);
    mod_resourceservice = require('./resourceservice.js')(config, mod_resourcemanager, mod_cache);
    mod_tagmanager.setTagList(config.taglib);
    mod_modules = require('./modules.js')(mod_tagmanager, mod_resourcemanager);

    if (config.remotecontrol) {
        logger.info('setting up remote control');
        redisclient = require('./redisclient.js');
        redisclient.init(mod_tagmanager);
    }

    createServer(config);
    logger.info('server started on port ' + config.server.port + ', ' + new Date());
});

function createServer(config) {
    var server = mod_connect.createServer(
        mod_connect.favicon()
        , mod_connect.bodyParser()
        , mod_connect.router(function (app) {
                app.get(/^\/modules\/([^\.]*)\/(.*\.js)$/,                  mod_modules.handleResourceRequest);
                app.get(/^\/modules\/([^\.]*)\/(.*\.html)$/,                mod_modules.handleViewRequest);
                app.get(/^\/modules\/([^\.]*)\/controller.*\/(.*\.js)$/,    mod_modules.handleControllerRequest);
                app.put(/^\/modules\/([^\.]*)\/controller.*\/(.*\.js)$/,    mod_modules.handleControllerRequest);
                app.post(/^\/modules\/([^\.]*)\/controller.*\/(.*\.js)$/,   mod_modules.handleControllerRequest);
                app.delete(/^\/modules\/([^\.]*)\/controller.*\/(.*\.js)$/, mod_modules.handleControllerRequest);
                app.get(/^\/resources(.*)$/,                                mod_resourceservice.handleRequest);
                //app.get(/instances\/(.*)/,                                  mod_instances.handleInstanceRequest);
            }
        )
        , mod_connect.static(config.server.documentRoot)
        , mod_connect.logger()
    );

    if (config.websockets) {
        logger.info('starting websockets');
        var io = require('socket.io').listen(server);
        mod_socketio.init(io);
    }
    server.listen(config.server.port);
}

// process.on('SIGINT', function () {
//  process.exit();
// });<commit_msg>rm static resource delivery, use connect static server via symlink<commit_after>"use strict";

/**
 * Main server.
 * Handles a very simple kind of dependency injection, to be refactored to some service
 * registry ting.
 *
 */

    var mod_connect         = require('connect')
    , mod_sys               = require('sys')
    , mod_path              = require('path')
    , mod_resourceservice   = null
    , mod_resourcemanager   = null
    , mod_tagmanager        = require('./tagmanager.js')
    , mod_cache             = require('./cache.js')
    , mod_socketio          = require('./socketio.js')
    , mod_modules           = null
    , mod_fs                = require('fs')
    , cache                 = null
    , redisclient           = null
    , logger                = require('./logger.js').getLogger(mod_path.basename(module.filename))
    , mod_modulecontainer       = null

if (process.argv.length < 3) {
    logger.error('usage: server <configfile>');
    process.exit();
}

// [TBD] handle arguments properly
// [TBD] dynamic config service (requires dependency management first)
var config = null;
logger.info('reading config from ' + process.argv[2]);
mod_fs.readFile(process.argv[2], function (err, data) {
    if (err) {
        logger.error('error reading configuration');
        process.exit();
    }
    config = JSON.parse(data);
    logger.info('config loaded');

    mod_modulecontainer = require('./modulecontainer.js')(config.componentconfigfile)

    mod_cache.configure(config);
    mod_resourcemanager = require('./resourcemanager.js')(config, mod_cache);
    mod_resourceservice = require('./resourceservice.js')(config, mod_resourcemanager, mod_cache);
    mod_tagmanager.setTagList(config.taglib);
    mod_modules = require('./modules.js')(mod_tagmanager, mod_resourcemanager, mod_modulecontainer);

    if (config.remotecontrol) {
        logger.info('setting up remote control');
        redisclient = require('./redisclient.js');
        redisclient.init(mod_tagmanager);
    }

    createServer(config);
    logger.info('server started on port ' + config.server.port + ', ' + new Date());
});

function createServer(config) {
    var server = mod_connect.createServer(
        mod_connect.favicon()
        , mod_connect.bodyParser()
        , mod_connect.router(function (app) {
                //app.get(/^\/modules\/([^\.]*)\/(.*\.js)$/,                  mod_modules.handleResourceRequest);
                app.get(/^\/modules\/([^\.]*)\/(.*\.html)$/,                mod_modules.handleViewRequest);
                app.get(/^\/modules\/([^\.]*)\/controller.*\/(.*\.js)$/,    mod_modules.handleControllerRequest);
                app.put(/^\/modules\/([^\.]*)\/controller.*\/(.*\.js)$/,    mod_modules.handleControllerRequest);
                app.post(/^\/modules\/([^\.]*)\/controller.*\/(.*\.js)$/,   mod_modules.handleControllerRequest);
                app.delete(/^\/modules\/([^\.]*)\/controller.*\/(.*\.js)$/, mod_modules.handleControllerRequest);
                app.get(/^\/resources(.*)$/,                                mod_resourceservice.handleRequest);
                //app.get(/instances\/(.*)/,                                  mod_instances.handleInstanceRequest);
            }
        )
        , mod_connect.static(config.server.documentRoot)
        , mod_connect.logger()
    );

    if (config.websockets) {
        logger.info('starting websockets');
        var io = require('socket.io').listen(server);
        mod_socketio.init(io);
    }
    server.listen(config.server.port);
}

// process.on('SIGINT', function () {
//  process.exit();
// });<|endoftext|>


<commit_before>var assert = require('assert');
var dgram = require('dgram');
var EventEmitter = require('events').EventEmitter;
var util = require('util');

var Query = require('./query');
var DnsError = require('./errors');

///--- Globals

var sprintf = util.format;

var ExceptionError = DnsError.ExceptionError;
var ProtocolError = DnsError.ProtocolError;

///--- API

function Server(options) {
        if (typeof(options) !== 'object')
                throw new TypeError('options (object) is required');

        this._log = options.log.child({component: 'agent'}, true);
        this._name = options.name || "named";
        this._socket = null;

}
util.inherits(Server, EventEmitter);

Server.prototype.close = function close(callback) {
        if (typeof (callback) === 'function')
                this._socket.once('close', callback);

        this._socket.close();
};

Server.prototype.listen = function listen(port, address, callback) {
        if (!port)
                throw new TypeError('port (number) is required');

        if (typeof (address) === 'function' || !address) {
                callback = address;
                address = '0.0.0.0';
        }

        var self = this;

        this._socket = dgram.createSocket('udp6');
        this._socket.once('listening', function () {
                self.emit('listening');
                if (typeof (callback) === 'function')
                        process.nextTick(callback);
        });
        this._socket.on('close', function onSocketClose() {
                self.emit('close');
        });
        this._socket.on('error', function onSocketError(err) {
                self.emit('error', err);
        });
        this._socket.on('message', function (buffer, rinfo) {
                var decoded;
                var query;
                var raw = {
                        buf: buffer,
                        len: rinfo.size
                };

                var src = {
                        family: 'udp6',
                        address: rinfo.address,
                        port: rinfo.port
                };

                try {
                        decoded = Query.parse(raw, src);
                        query = Query.createQuery(decoded);
                } catch (e) {
                        self.emit('clientError',
                                  new ProtocolError('invalid DNS datagram'));
                }

                if (query === undefined || query === null) {
                        return;
                }

                query.respond = function respond() {
                        self.send(query);
                };

                try {
                        self.emit('query', query);
                } catch (e) {
                        self._log.warn({
                                err: e
                        }, 'query handler threw an uncaughtException');
                        self.emit('uncaughtException', e);
                }
        });
        this._socket.bind(port, address);
};

Server.prototype.send = function send(res) {
        assert.ok(res);

        try {
                res._flags.qr = 1;  // replace with function
                res.encode();
        } catch (e) {
                this._log.trace({err: e}, 'send: uncaughtException');
                var err = new ExceptionError('unable to encode response');
                this.emit('uncaughtException', err);
                return false;
        }

        var addr = res._client.address;
        var buf = res._raw.buf;
        var len = res._raw.len;
        var port = res._client.port;
        var self = this;

        this._log.trace({
                adddress: addr,
                port: port,
                len: len
        }, 'send: writing DNS message to socket');

        this._socket.send(buf, 0, len, port, addr, function (err, bytes) {
                if (err) {
                        self._log.warn({
                                adddress: addr,
                                port: port,
                                err: err
                        }, 'send: unable to send response');
                        self.emit('error', new ExceptionError(err.message));
                } else {
                        self._log.trace({
                                adddress: addr,
                                port: port
                        }, 'send: DNS response sent');
                        self.emit('after', res, bytes);
                }
        });
};

Server.prototype.toString = function toString() {
        var str = '[object named.Server <';
        str += sprintf('name=%s, ', this._name);
        str += sprintf('socket=%j', this._socket ? this._socket.address() : {});
        str += '>]';
        return (str);
};

///--- Exports

module.exports = Server;
<commit_msg>Basic TCP support<commit_after>var assert = require('assert');
var dgram = require('dgram');
var net = require('net');
var EventEmitter = require('events').EventEmitter;
var util = require('util');

var Query = require('./query');
var DnsError = require('./errors');

///--- Globals

var sprintf = util.format;

var ExceptionError = DnsError.ExceptionError;
var ProtocolError = DnsError.ProtocolError;

///--- API

function Server(options) {
        if (typeof(options) !== 'object')
                throw new TypeError('options (object) is required');

        this._log = options.log.child({component: 'agent'}, true);
        this._name = options.name || "named";
        this._socket = null;

}
util.inherits(Server, EventEmitter);

Server.prototype.close = function close(callback) {
        if (typeof (callback) === 'function')
                this._socket.once('close', callback);

        this._socket.close();
};

Server.prototype.listenTcp = function listenTcp(port, address, callback) {
        if (!port)
                throw new TypeError('port (number) is required');

        if (typeof (address) === 'function' || !address) {
                callback = address;
                address = '0.0.0.0';
        }

        var self = this;
        this._server = net.createServer();
        this._server.listen(port, address);
        this._server.on('close', function onSocketClose() {
                self.emit('close');
        });
        this._server.on('error', function onSocketError(err) {
                self.emit('error', err);
        });
        this._server.on('connection', function(sock) {
                var qbuf = new Buffer(0);
                sock.on('error', function onSocketError(err) {
                        self.emit('error', err);
                });
                sock.on('data', function(data) {
                        qbuf = Buffer.concat([qbuf, data]);

                        if (qbuf.length < 2)
                                return;
                        var len = qbuf.readUInt16BE(0);
                        if (qbuf.length - 2 < len)
                                return;

                        qbuf = qbuf.slice(2);

                        var decoded;
                        var query;
                        var raw = {
                                buf: qbuf,
                                len: qbuf.length
                        };
                        var src = {
                                family: 'tcp',
                                address: sock.remoteAddress,
                                socket: sock
                        };
                        try {
                                decoded = Query.parse(raw, src);
                                query = Query.createQuery(decoded);
                        } catch (e) {
                                console.log(e);
                                return;
                        }

                        if (query === undefined || query === null) {
                                return;
                        }

                        sock.on("end", function() {
                                delete query._client.socket;
                        });
                        sock.on("close", function() {
                                delete query._client.socket;
                        });

                        query.respond = function respond() {
                                self.send(query);
                        };

                        try {
                                self.emit('query', query);
                        } catch (e) {
                                self._log.warn({
                                        err: e
                                }, 'query handler threw an uncaughtException');
                                self.emit('uncaughtException', e);
                        }
                });
                sock.on('close', function() {
                        qbuf = undefined;
                })
        });
        this._server.on('close', function() {
                self.emit('close');
        })
}

Server.prototype.listen = function listen(port, address, callback) {
        if (!port)
                throw new TypeError('port (number) is required');

        if (typeof (address) === 'function' || !address) {
                callback = address;
                address = '0.0.0.0';
        }

        var self = this;

        this._socket = dgram.createSocket('udp6');
        this._socket.once('listening', function () {
                self.emit('listening');
                if (typeof (callback) === 'function')
                        process.nextTick(callback);
        });
        this._socket.on('close', function onSocketClose() {
                self.emit('close');
        });
        this._socket.on('error', function onSocketError(err) {
                self.emit('error', err);
        });
        this._socket.on('message', function (buffer, rinfo) {
                var decoded;
                var query;
                var raw = {
                        buf: buffer,
                        len: rinfo.size
                };

                var src = {
                        family: 'udp6',
                        address: rinfo.address,
                        port: rinfo.port
                };

                try {
                        decoded = Query.parse(raw, src);
                        query = Query.createQuery(decoded);
                } catch (e) {
                        self.emit('clientError',
                                  new ProtocolError('invalid DNS datagram'));
                }

                if (query === undefined || query === null) {
                        return;
                }

                query.respond = function respond() {
                        self.send(query);
                };

                try {
                        self.emit('query', query);
                } catch (e) {
                        self._log.warn({
                                err: e
                        }, 'query handler threw an uncaughtException');
                        self.emit('uncaughtException', e);
                }
        });
        this._socket.bind(port, address);
};

Server.prototype.send = function send(res) {
        assert.ok(res);

        try {
                res._flags.qr = 1;  // replace with function
                res.encode();
        } catch (e) {
                this._log.trace({err: e}, 'send: uncaughtException');
                var err = new ExceptionError('unable to encode response');
                this.emit('uncaughtException', err);
                return false;
        }

        var family = res._client.family;
        switch (family) {
                case 'udp6':
                        var addr = res._client.address;
                        var buf = res._raw.buf;
                        var len = res._raw.len;
                        var port = res._client.port;
                        var self = this;

                        this._log.trace({
                                adddress: addr,
                                port: port,
                                len: len
                        }, 'send: writing DNS message to socket');

                        this._socket.send(buf, 0, len, port, addr, function (err, bytes) {
                                if (err) {
                                        self._log.warn({
                                                adddress: addr,
                                                port: port,
                                                err: err
                                        }, 'send: unable to send response');
                                        self.emit('error', new ExceptionError(err.message));
                                } else {
                                        self._log.trace({
                                                adddress: addr,
                                                port: port
                                        }, 'send: DNS response sent');
                                        self.emit('after', res, bytes);
                                }
                        });
                        break;

                case 'tcp':
                        var sock = res._client.socket;
                        if (!sock) {
                                this._log.debug("send: connection already closed, dropping data");
                                break;
                        }

                        var buf = res._raw.buf;
                        var len = res._raw.len;
                        var self = this;
                        this._log.trace({
                                adddress: sock.remoteAddress,
                                len: len
                        }, 'send: writing DNS message to TCP socket');
                        var lenbuf = new Buffer(2);
                        lenbuf.writeUInt16BE(buf.length, 0);
                        sock.write(Buffer.concat([lenbuf, buf]));
                        break;
        }
};

Server.prototype.toString = function toString() {
        var str = '[object named.Server <';
        str += sprintf('name=%s, ', this._name);
        str += sprintf('socket=%j', this._socket ? this._socket.address() : {});
        str += '>]';
        return (str);
};

///--- Exports

module.exports = Server;
<|endoftext|>


<commit_before>/*
 * Shashi, a simple module to generate, using pseudo-randomness,
 * a universal family/set of hash functions, which produce integer
 * values within the selected range (a prime number).
 *
 * Copyright(c) 2015 Guglielmo Ferri <44gatti@gmail.com>
 * MIT Licensed
 */

exports.version = require( '../package' ).version;
exports.Shashi = ( function () {
    var abs = Math.abs
        , ceil = Math.ceil
        , sqrt = Math.sqrt
        , emptyFn = function () {}
        , isBuffer = Buffer.isBuffer
        , Brando = require( 'brando' )
        // primality test
        , composite = function ( n ) {
            var p = n >>> 0 ? abs( n ) : 1
                , qx = ceil( sqrt( p ) )
                , i = 2
                ;
            // skip trivial numbers
            if ( n < 4 ) return 0;
            // search for divisors
            for ( ; i <= qx; ++i ) if ( p % i === 0 ) return i;
            return 0;
        }
        ;
    return function ( fk, items, prange, random_data, callback, group ) {
        var cback = typeof callback === 'function' ? callback : emptyFn
            , div = composite( prange )
            , emsg = div ? + prange + ' is not a prime number! (divisor: ' + div + ')' : null
            ;
        // prange should be a prime number
        if ( div )
            if ( isBuffer( random_data ) ) return cback( new Error( emsg ) );
            else throw new Error( emsg );
        // generate random seed sequence for requested range
        var  k = abs( fk ) || 1
            , r = prange - 1
            , i = items >>> 0 ? abs( items ) : 1
            , len = k * i
            , seq = Brando.emt( len, r )
            , ibytes = seq.ibytes
            , result = seq.result
            , ruint = seq.ruint
            , rlen = result.length
            , slen = i * ibytes
            // universal family/set of hash functions
            , fn = function ( n, buffer, bytes ) {
                var s = ( n % k ) * slen
                    // how many bytes to read/consume from the input buffer [1 to 4]
                    , rbytes = bytes >>> 0 ? abs( bytes ) % 5 : ibytes
                    , blen = buffer.length - rbytes
                    , bruint = null
                    , sum = 0
                    , b = 0
                    ;
                /*
                 * NOTE: js is not capable to handle integers larger than ~2^53,
                 * when the sum exceeds this limit, we expect to get get biased
                 * results, or in other words, more collisions. It will happens
                 * when we are using large primes as range, or with long buffer
                 * in input. If sum === sum + 1, we could break the for cycle.
                 */
                if ( rbytes === 1 ) {
                    // use [] notation when reading input, 1 byte at the time.
                    if ( ibytes === 1 ) {
                        // read input and seed 1 byte at the time
                        for ( ; b < blen && s < rlen; ++b, ++s )
                            sum += buffer[ b ] * result[ s ];
                        return sum % prange;
                    }
                    for ( ; b < blen && s < rlen; ++b, s += ibytes )
                        sum += buffer[ b ] * result[ ruint ]( s );
                    return sum % prange;
                }
                // build string for read method (16, 24 or 32 bits unsigned integers)
                bruint =  bruint = 'readUInt' + ( rbytes << 3 ) + 'BE';
                for ( ; b < blen && s < rlen; b += rbytes, s += ibytes ) {
                    sum += buffer[ bruint ]( b ) * result[ ruint ]( s );
                    sum %= prange;
                }
                return sum;
            }
            ;
        /*
         * When a Buffer is not supplied, it fills the seed sequence through Math.random(),
         * then, it returns an Array composed by the universal hash function and the random
         * seed sequence; otherwise it switches to callback mode.
         */
        if ( ! isBuffer( random_data ) && seq.fill() ) return [ fn, seq ];
        // callback mode, set listeners for sequence
        seq.on( 'fart', function () {
            cback( null, fn, seq );
        } );
        seq.on( 'feed', function ( mbytes ) {
            var emsg = 'there are not enough random data for generating seed sequence! (' + mbytes + ' bytes remaining)'
                ;
            cback( new Error( emsg ) );
        } );
        // parse input data
        return seq.parse( random_data );
    }
    ;

} )();<commit_msg>removed unused var.. On branch master modified:   lib/shashi.js<commit_after>/*
 * Shashi, a simple module to generate, using pseudo-randomness,
 * a universal family/set of hash functions, which produce integer
 * values within the selected range (a prime number).
 *
 * Copyright(c) 2015 Guglielmo Ferri <44gatti@gmail.com>
 * MIT Licensed
 */

exports.version = require( '../package' ).version;
exports.Shashi = ( function () {
    var abs = Math.abs
        , ceil = Math.ceil
        , sqrt = Math.sqrt
        , emptyFn = function () {}
        , isBuffer = Buffer.isBuffer
        , Brando = require( 'brando' )
        // primality test
        , composite = function ( n ) {
            var p = n >>> 0 ? abs( n ) : 1
                , qx = ceil( sqrt( p ) )
                , i = 2
                ;
            // skip trivial numbers
            if ( n < 4 ) return 0;
            // search for divisors
            for ( ; i <= qx; ++i ) if ( p % i === 0 ) return i;
            return 0;
        }
        ;
    return function ( fk, items, prange, random_data, callback ) {
        var cback = typeof callback === 'function' ? callback : emptyFn
            , div = composite( prange )
            , emsg = div ? + prange + ' is not a prime number! (divisor: ' + div + ')' : null
            ;
        // prange should be a prime number
        if ( div )
            if ( isBuffer( random_data ) ) return cback( new Error( emsg ) );
            else throw new Error( emsg );
        // generate random seed sequence for requested range
        var  k = abs( fk ) || 1
            , r = prange - 1
            , i = items >>> 0 ? abs( items ) : 1
            , len = k * i
            , seq = Brando.emt( len, r )
            , ibytes = seq.ibytes
            , result = seq.result
            , ruint = seq.ruint
            , rlen = result.length
            , slen = i * ibytes
            // universal family/set of hash functions
            , fn = function ( n, buffer, bytes ) {
                var s = ( n % k ) * slen
                    // how many bytes to read/consume from the input buffer [1 to 4]
                    , rbytes = bytes >>> 0 ? abs( bytes ) % 5 : ibytes
                    , blen = buffer.length - rbytes
                    , bruint = null
                    , sum = 0
                    , b = 0
                    ;
                /*
                 * NOTE: js is not capable to handle integers larger than ~2^53,
                 * when the sum exceeds this limit, we expect to get get biased
                 * results, or in other words, more collisions. It will happens
                 * when we are using large primes as range, or with long buffer
                 * in input. If sum === sum + 1, we could break the for cycle.
                 */
                if ( rbytes === 1 ) {
                    // use [] notation when reading input, 1 byte at the time.
                    if ( ibytes === 1 ) {
                        // read input and seed 1 byte at the time
                        for ( ; b < blen && s < rlen; ++b, ++s )
                            sum += buffer[ b ] * result[ s ];
                        return sum % prange;
                    }
                    for ( ; b < blen && s < rlen; ++b, s += ibytes )
                        sum += buffer[ b ] * result[ ruint ]( s );
                    return sum % prange;
                }
                // build string for read method (16, 24 or 32 bits unsigned integers)
                bruint =  bruint = 'readUInt' + ( rbytes << 3 ) + 'BE';
                for ( ; b < blen && s < rlen; b += rbytes, s += ibytes ) {
                    sum += buffer[ bruint ]( b ) * result[ ruint ]( s );
                    sum %= prange;
                }
                return sum;
            }
            ;
        /*
         * When a Buffer is not supplied, it fills the seed sequence through Math.random(),
         * then, it returns an Array composed by the universal hash function and the random
         * seed sequence; otherwise it switches to callback mode.
         */
        if ( ! isBuffer( random_data ) && seq.fill() ) return [ fn, seq ];
        // callback mode, set listeners for sequence
        seq.on( 'fart', function () {
            cback( null, fn, seq );
        } );
        seq.on( 'feed', function ( mbytes ) {
            var emsg = 'there are not enough random data for generating seed sequence! (' + mbytes + ' bytes remaining)'
                ;
            cback( new Error( emsg ) );
        } );
        // parse input data
        return seq.parse( random_data );
    }
    ;

} )();<|endoftext|>


<commit_before>'use strict';

const NodeBB = require('./nodebb');

const Shouts = module.exports;

Shouts.addShout = async function (fromuid, content) {
	const sid = await NodeBB.db.incrObjectField('global', 'nextSid');
	const shout = {
		sid: sid,
		content: content,
		timestamp: Date.now(),
		fromuid: fromuid,
		deleted: '0',
	};
	await Promise.all([
		NodeBB.db.setObject(`shout:${sid}`, shout),
		NodeBB.db.listAppend('shouts', sid),
	]);
	return await getShouts([sid]);
};

Shouts.getPlainShouts = async function (sids) {
	const keys = sids.map(sid => `shout:${sid}`);
	const shouts = await NodeBB.db.getObjects(keys);
	return addSids(shouts, sids);
};

function addSids(shouts, sids) {
	shouts.forEach((s, index) => {
		if (s && !s.hasOwnProperty('sid')) {
			s.sid = sids[index];
		}
	});
	return shouts;
}

Shouts.getShouts = async function (start, end) {
	const sids = await NodeBB.db.getListRange('shouts', start, end);
	if (!Array.isArray(sids) || !sids.length) {
		return [];
	}

	return await getShouts(sids);
};

async function getShouts(sids) {
	const keys = sids.map(sid => `shout:${sid}`);
	const shouts = await NodeBB.db.getObjects(keys);
	addSids(shouts, sids);

	// Get a list of unique uids of the users of non-deleted shouts
	const uniqUids = shouts.map(s => (parseInt(s.deleted, 10) !== 1 ? parseInt(s.fromuid, 10) : null))
		.filter((u, index, self) => (u === null ? false : self.indexOf(u) === index));

	const usersData = await NodeBB.User.getUsersFields(uniqUids, ['uid', 'username', 'userslug', 'picture', 'status']);
	const uidToUserData = {};
	uniqUids.forEach((uid, index) => {
		uidToUserData[uid] = usersData[index];
	});
	return await Promise.all(shouts.map(async (shout) => {
		if (parseInt(shout.deleted, 10) === 1) {
			return shout;
		}

		const userData = uidToUserData[parseInt(shout.fromuid, 10)];

		const s = await Shouts.parse(shout.content, userData);
		shout.user = s.user;
		shout.content = s.content;
		return shout;
	}));
}

Shouts.parse = async function (raw, userData) {
	const [parsed, isAdmin, isMod, status] = await Promise.all([
		NodeBB.Plugins.hooks.fire('filter:parse.raw', raw),
		NodeBB.User.isAdministrator(userData.uid),
		NodeBB.User.isGlobalModerator(userData.uid),
		NodeBB.User.isOnline(userData.uid),
	]);

	userData.status = status ? (userData.status || 'online') : 'offline';
	userData.isAdmin = isAdmin;
	userData.isMod = isMod;
	return {
		user: userData,
		content: parsed,
	};
};

Shouts.removeShout = async function (sid, uid) {
	const [isAdmin, isMod, fromUid] = await Promise.all([
		NodeBB.User.isAdministrator(uid),
		NodeBB.User.isGlobalModerator(uid),
		NodeBB.db.getObjectField(`shout:${sid}`, 'fromuid'),
	]);

	if (isAdmin || isMod || parseInt(fromUid, 10) === parseInt(uid, 10)) {
		await NodeBB.db.setObjectField(`shout:${sid}`, 'deleted', '1');
		return true;
	}
	throw new Error('[[error:no-privileges]]');
};

Shouts.editShout = async function (sid, msg, uid) {
	const [isAdmin, isMod, fromUid] = await Promise.all([
		NodeBB.User.isAdministrator(uid),
		NodeBB.User.isGlobalModerator(uid),
		NodeBB.db.getObjectField(`shout:${sid}`, 'fromuid'),
	]);

	if (isAdmin || isMod || parseInt(fromUid, 10) === parseInt(uid, 10)) {
		await NodeBB.db.setObjectField(`shout:${sid}`, 'content', msg);
		return await getShouts([sid]);
	}
	throw new Error('[[error:no-privileges]]');
};

Shouts.pruneDeleted = async function (uid) {
	const isAdmin = await NodeBB.User.isAdministrator(uid);
	if (!isAdmin) {
		throw new Error('[[error:no-privileges]]');
	}

	const sids = await NodeBB.db.getListRange('shouts', 0, -1);
	if (!sids || !sids.length) {
		return;
	}

	const keys = sids.map(sid => `shout:${sid}`);
	const items = await NodeBB.db.getObjectsFields(keys, ['deleted']);
	const toDelete = [];
	items.forEach((shout, index) => {
		shout.sid = sids[index];
		if (parseInt(shout.deleted, 10) === 1) {
			toDelete.push(shout);
		}
	});

	await Promise.all([
		NodeBB.db.listRemoveAll('shouts', toDelete.map(s => s.sid)),
		NodeBB.db.deleteAll(toDelete.map(s => `shout:${s.sid}`)),
	]);
	return true;
};

Shouts.removeAll = async function (uid) {
	const isAdmin = await NodeBB.User.isAdministrator(uid);
	if (!isAdmin) {
		throw new Error('not-authorized');
	}

	const sids = await NodeBB.db.getListRange('shouts', 0, -1);
	if (!sids || !sids.length) {
		return;
	}

	const keys = sids.map(sid => `shout:${sid}`);

	await Promise.all([
		NodeBB.db.deleteAll(keys),
		NodeBB.db.delete('shouts'),
		NodeBB.db.setObjectField('global', 'nextSid', 0),
	]);
	return true;
};
<commit_msg>fix: deleted shouts<commit_after>'use strict';

const NodeBB = require('./nodebb');

const Shouts = module.exports;

Shouts.addShout = async function (fromuid, content) {
	const sid = await NodeBB.db.incrObjectField('global', 'nextSid');
	const shout = {
		sid: sid,
		content: content,
		timestamp: Date.now(),
		fromuid: fromuid,
		deleted: '0',
	};
	await Promise.all([
		NodeBB.db.setObject(`shout:${sid}`, shout),
		NodeBB.db.listAppend('shouts', sid),
	]);
	return await getShouts([sid]);
};

Shouts.getPlainShouts = async function (sids) {
	const keys = sids.map(sid => `shout:${sid}`);
	const shouts = await NodeBB.db.getObjects(keys);
	return addSids(shouts, sids);
};

function addSids(shouts, sids) {
	shouts.forEach((s, index) => {
		if (s && !s.hasOwnProperty('sid')) {
			s.sid = sids[index];
		}
	});
	return shouts;
}

Shouts.getShouts = async function (start, end) {
	const sids = await NodeBB.db.getListRange('shouts', start, end);
	if (!Array.isArray(sids) || !sids.length) {
		return [];
	}

	return await getShouts(sids);
};

async function getShouts(sids) {
	const keys = sids.map(sid => `shout:${sid}`);
	const shouts = await NodeBB.db.getObjects(keys);
	addSids(shouts, sids);

	// Get a list of unique uids of the users of non-deleted shouts
	const uniqUids = shouts.map(s => (parseInt(s.deleted, 10) !== 1 ? parseInt(s.fromuid, 10) : null))
		.filter((u, index, self) => (u === null ? false : self.indexOf(u) === index));

	const usersData = await NodeBB.User.getUsersFields(uniqUids, ['uid', 'username', 'userslug', 'picture', 'status']);
	const uidToUserData = {};
	uniqUids.forEach((uid, index) => {
		uidToUserData[uid] = usersData[index];
	});
	return await Promise.all(shouts.map(async (shout) => {
		if (parseInt(shout.deleted, 10) === 1) {
			return null;
		}

		const userData = uidToUserData[parseInt(shout.fromuid, 10)];

		const s = await Shouts.parse(shout.content, userData);
		shout.user = s.user;
		shout.content = s.content;
		return shout;
	}));
}

Shouts.parse = async function (raw, userData) {
	const [parsed, isAdmin, isMod, status] = await Promise.all([
		NodeBB.Plugins.hooks.fire('filter:parse.raw', raw),
		NodeBB.User.isAdministrator(userData.uid),
		NodeBB.User.isGlobalModerator(userData.uid),
		NodeBB.User.isOnline(userData.uid),
	]);

	userData.status = status ? (userData.status || 'online') : 'offline';
	userData.isAdmin = isAdmin;
	userData.isMod = isMod;
	return {
		user: userData,
		content: parsed,
	};
};

Shouts.removeShout = async function (sid, uid) {
	const [isAdmin, isMod, fromUid] = await Promise.all([
		NodeBB.User.isAdministrator(uid),
		NodeBB.User.isGlobalModerator(uid),
		NodeBB.db.getObjectField(`shout:${sid}`, 'fromuid'),
	]);

	if (isAdmin || isMod || parseInt(fromUid, 10) === parseInt(uid, 10)) {
		await NodeBB.db.setObjectField(`shout:${sid}`, 'deleted', '1');
		return true;
	}
	throw new Error('[[error:no-privileges]]');
};

Shouts.editShout = async function (sid, msg, uid) {
	const [isAdmin, isMod, fromUid] = await Promise.all([
		NodeBB.User.isAdministrator(uid),
		NodeBB.User.isGlobalModerator(uid),
		NodeBB.db.getObjectField(`shout:${sid}`, 'fromuid'),
	]);

	if (isAdmin || isMod || parseInt(fromUid, 10) === parseInt(uid, 10)) {
		await NodeBB.db.setObjectField(`shout:${sid}`, 'content', msg);
		return await getShouts([sid]);
	}
	throw new Error('[[error:no-privileges]]');
};

Shouts.pruneDeleted = async function (uid) {
	const isAdmin = await NodeBB.User.isAdministrator(uid);
	if (!isAdmin) {
		throw new Error('[[error:no-privileges]]');
	}

	const sids = await NodeBB.db.getListRange('shouts', 0, -1);
	if (!sids || !sids.length) {
		return;
	}

	const keys = sids.map(sid => `shout:${sid}`);
	const items = await NodeBB.db.getObjectsFields(keys, ['deleted']);
	const toDelete = [];
	items.forEach((shout, index) => {
		shout.sid = sids[index];
		if (parseInt(shout.deleted, 10) === 1) {
			toDelete.push(shout);
		}
	});

	await Promise.all([
		NodeBB.db.listRemoveAll('shouts', toDelete.map(s => s.sid)),
		NodeBB.db.deleteAll(toDelete.map(s => `shout:${s.sid}`)),
	]);
	return true;
};

Shouts.removeAll = async function (uid) {
	const isAdmin = await NodeBB.User.isAdministrator(uid);
	if (!isAdmin) {
		throw new Error('not-authorized');
	}

	const sids = await NodeBB.db.getListRange('shouts', 0, -1);
	if (!sids || !sids.length) {
		return;
	}

	const keys = sids.map(sid => `shout:${sid}`);

	await Promise.all([
		NodeBB.db.deleteAll(keys),
		NodeBB.db.delete('shouts'),
		NodeBB.db.setObjectField('global', 'nextSid', 0),
	]);
	return true;
};
<|endoftext|>


<commit_before>/*
 * sjedon
 * https://github.com/fabiosantoscode/sjedon
 *
 * Copyright (c) Fábio Santos 2014
 * Licensed under the MIT license.
 */

'use strict';

var assert = require('assert')
var escope = require('escope')
var EventEmitter = require('events').EventEmitter;

var nothing = {};
var break_ = {};

function notimplemented(feature) {
    throw new Error ('not implemented: ' + (feature ? ': ' + feature : ''));
}

Sjedon.StackFrame = StackFrame
function StackFrame(options) {
    var sjedon = options.sjedon, ast = options.ast, parent = options.parent;
    assert(sjedon, 'Must pass reference to Sjedon')
    assert(ast, 'Must pass the AST with the function')
    assert(parent !== undefined, 'Parent argument mustn\'t be undefined.')
    assert(sjedon.getScope(ast), 'cannot add a stack frame to a ' + ast.type)

    this.ast = ast
    this.sjedon = sjedon

    var variables = this.variables = {}
    sjedon.scopeVariables(ast).forEach(function (name) {
        variables[name] = undefined;
    })
    this.parent = parent
}

StackFrame.prototype.fetchVar = function (name) {
    if (name in this.variables) {
        return this.variables[name]
    } else if (this.parent) {
        return this.parent.fetchVar(name)
    } else if (name in this.sjedon.global) {
        return this.sjedon.global[name];
    }
    throw new Error('variable "' + name + '" not found')
}
StackFrame.prototype.assignVar = function (name, value) {
    if (name in this.variables) {
        return this.variables[name] = value;
    } else if (this.parent) {
        return this.parent.assignVar(name, value)
    }
    throw new Error('undefined reference: ' + value)
}
StackFrame.prototype.trace = function () {
    var ret = [{ frame: this }]
    if (this.parent) { return this.parent.trace().concat(ret); }
    return ret;
}

function Sjedon(ast, options) {
    if (!(this instanceof Sjedon)) { return new Sjedon(ast); }
    this.ast = ast;
    this.scopeManager = escope.analyze(ast);
    this.scopeManager.attach(); // attach to AST
    this.globalFrame = new Sjedon.StackFrame({
        sjedon: this,
        ast: ast,
        parent: null
    });
    this.currentFrame = this.globalFrame
    this.global = (options && options.global) || {};
}

Sjedon.prototype = new EventEmitter()

Sjedon.prototype.addParents = function (ast) {
    for (var key in ast) if (ast.hasOwnProperty(key)) {
        if (typeof ast[key] === 'object' && ast[key] && !/^_/.test(key)) {
            ast[key]._parent = ast;
            this.addParents(ast[key]);
        }
    }
}

Sjedon.prototype.run = function (block) {
    this.evalBlock(block || this.ast.body);
}

Sjedon.prototype.evalBlock = function (body) {
    var ret = nothing;

    assert(body && typeof body.length === 'number', 'evalBlock: "' + body + '" is not an array of statements')

    for (var i = 0, len = body.length; i < len; i++) {
        ret = this.evalStatement(body[i])
        if (ret !== nothing) { return ret; }
    }

    return nothing;
}

Sjedon.prototype.evalStatement = function (statement) {
    if (statement.type === 'ExpressionStatement') {
        return this.evalExpression(statement.expression);
    } else if(statement.type === 'BlockStatement') {
        return this.evalBlock(statement.body);
    } else if (statement.type === 'ReturnStatement') {
        if (statement.argument !== null) {
            return this.evalExpression(statement.argument)
        }
        return nothing;
    } else if (statement.type === 'BreakStatement') {
        if (statement.label) { notimplemented('Labeled break statement'); }
        return break_;
    } else if (statement.type === 'IfStatement') {
        if (this.evalExpression(statement.test)) {
            return this.evalStatement(statement.consequent);
        } else if (statement.alternate) {
            return this.evalStatement(statement.alternate);
        } else {
            return nothing;
        }
    } else if (statement.type === 'SwitchStatement') {
        var discriminant = this.evalExpression(statement.discriminant);
        var kase;

        for (var i = 0, len = statement.cases.length; i < len; i++) {
            kase = statement.cases[i];
            assert(kase.type === 'SwitchCase');
            if (kase.test === null /* default: */ ||
                    this.evalExpression(kase.test) === discriminant) {
                break;
            }
        }

        for (;i < len; i++) {
            kase = statement.cases[i];
            var ret = this.evalBlock(kase.consequent);
            if (ret === nothing) {
                continue;
            } else if (ret === break_) {
                return nothing;
            } else {
                return ret;
            }
        }

        return nothing;
    } else if (statement.type === 'VariableDeclaration') {
        var self = this;
        statement.declarations.forEach(function (decl) {
            if (decl.init) {
                var initial = self.evalExpression(decl.init)
                self.currentFrame.assignVar(decl.id.name, initial);
            } // else, it's just a placeholder.
        })

        return nothing;
    } else if (statement.type === 'EmptyStatement') {
        return nothing;
    } else {
        notimplemented('statement "' + statement.type + '"');
    }
    assert(false, 'Sjedon#evalStatement must always return explicitly! If there is no "return", "break", or "throw", return a "nothing" reference');
};

Sjedon.prototype.arrayLiteral = function (contents) {
    return contents;
}

Sjedon.prototype.objectLiteral = function (props) {
    var ret = {};
    var len = props.length;
    while(len--) {
        ret[props[len][0]] = props[len][1];
    }
    return ret;
}

Sjedon.prototype.unaryExpression = function (op, argument) {
    if (op === 'void') {
        this.evalExpression(argument);
        return undefined;
    } else if (op === 'typeof') {
        var operand = this.evalExpression(argument);
        return typeof operand;
    }
}

Sjedon.prototype.ternaryExpression = function (test, consequent, alternate) {
    return this.evalExpression(this.evalExpression(test) ? consequent : alternate)
}

Sjedon.prototype.evalObjectKey = function (key) {
    if (key.type === 'Identifier') return key.name
    if (key.type === 'Literal') return key.value

    assert(key.type);
    assert(false, 'unsupported object key: ' + key.type);
}

Sjedon.prototype.evalExpression = function (expr) {
    if (expr.type === 'CallExpression') {
        return this.callFunction(expr.callee);
    } else if (expr.type === 'Literal') {
        return expr.value;
    } else if (expr.type === 'ArrayExpression') {
        return this.arrayLiteral(
            expr.elements.map(this.evalExpression.bind(this)))
    } else if (expr.type === 'ObjectExpression') {
        var self = this;
        return this.objectLiteral(expr.properties.map(function (prop) {
            return [
                self.evalObjectKey(prop.key),
                self.evalExpression(prop.value)
            ];
        }))
    } else if (expr.type === 'AssignmentExpression') {
        return this.currentFrame.assignVar(expr.left.name,
            this.evalExpression(expr.right))
    } else if (expr.type === 'SequenceExpression') {
        for (var i = 0; i < expr.expressions.length - 1; i++) {
            this.evalExpression(expr.expressions[i])
        }
        return this.evalExpression(expr.expressions[i]);
    } else if (expr.type === 'UnaryExpression') {
        return this.unaryExpression(expr.operator, expr.argument);
    } else if (expr.type === 'ConditionalExpression' ) {
        return this.ternaryExpression(expr.test, expr.consequent, expr.alternate);
    } else if (expr.type === 'Identifier') {
        return this.currentFrame.fetchVar(expr.name)
    } else {
        notimplemented('expression type "' + expr.type + '"')
    }
};

Sjedon.prototype.callFunction = function (callee) {
    if (callee.type === 'Identifier') {
        var func = this.currentFrame.fetchVar(callee.name)
        assert(func)
        if (typeof func === 'function') {
            func(null);  // TODO arguments, context
        } else {
            this.runFunction(func)
        }
    } else if (callee.type === 'FunctionExpression' || callee.type === 'FunctionDeclaration') {
        return this.runFunction(callee);
    } else {
        notimplemented('calling functions other than FunctionExpression (given ' + callee.type + ')')
    }
}

Sjedon.prototype.runFunction = function (functionAST) {
    var stackFrame = new Sjedon.StackFrame({
        sjedon: this,
        ast: functionAST,
        parent: this.currentFrame
    })

    this.currentFrame = stackFrame

    var ret = this.evalStatement(functionAST.body)

    this.currentFrame = stackFrame.parent

    return ret === nothing ?
        undefined :
        ret;
}

Sjedon.prototype.findScope = function (funcNode, name) {
    assert(funcNode && name, 'findScope arguments incomplete')
    var scope = this.getScope(funcNode)
    function findName(variable) { return variable.name === name }
    while (scope) {
        if (scope.variables.some(findName)) {
            return scope.block;
        }
        scope = scope.upper
    }
    return null
}

Sjedon.prototype.getScope = function (astNode) {
    return astNode[escope.Scope.mangledName]
}

Sjedon.prototype.scopeVariables = function (astNode) {
    return this.getScope(astNode)
        .variables
        .map(function(v) { return v.name })
}

Sjedon.prototype.trace = function () {
    return this.currentFrame.trace()
}

Sjedon.prototype.fetchVar = function (name) {
    var ret;
    if ((ret = this.currentFrame.fetchVar(name))) {
        return ret;
    } else if ((ret = this.global[name])) {
        return ret;
    }
}

module.exports = Sjedon;

<commit_msg>small refactor<commit_after>/*
 * sjedon
 * https://github.com/fabiosantoscode/sjedon
 *
 * Copyright (c) Fábio Santos 2014
 * Licensed under the MIT license.
 */

'use strict';

var assert = require('assert')
var escope = require('escope')
var EventEmitter = require('events').EventEmitter;

var nothing = {};
var break_ = {};

function notimplemented(feature) {
    throw new Error ('not implemented: ' + (feature ? ': ' + feature : ''));
}

Sjedon.StackFrame = StackFrame
function StackFrame(options) {
    var sjedon = options.sjedon, ast = options.ast, parent = options.parent;
    assert(sjedon, 'Must pass reference to Sjedon')
    assert(ast, 'Must pass the AST with the function')
    assert(parent !== undefined, 'Parent argument mustn\'t be undefined.')
    assert(sjedon.getScope(ast), 'cannot add a stack frame to a ' + ast.type)

    this.ast = ast
    this.sjedon = sjedon

    var variables = this.variables = {}
    sjedon.scopeVariables(ast).forEach(function (name) {
        variables[name] = undefined;
    })
    this.parent = parent
}

StackFrame.prototype.fetchVar = function (name) {
    if (name in this.variables) {
        return this.variables[name]
    } else if (this.parent) {
        return this.parent.fetchVar(name)
    } else if (name in this.sjedon.global) {
        return this.sjedon.global[name];
    }
    throw new Error('variable "' + name + '" not found')
}
StackFrame.prototype.assignVar = function (name, value) {
    if (name in this.variables) {
        return this.variables[name] = value;
    } else if (this.parent) {
        return this.parent.assignVar(name, value)
    }
    throw new Error('undefined reference: ' + value)
}
StackFrame.prototype.trace = function () {
    var ret = [{ frame: this }]
    if (this.parent) { return this.parent.trace().concat(ret); }
    return ret;
}

function Sjedon(ast, options) {
    if (!(this instanceof Sjedon)) { return new Sjedon(ast); }
    this.ast = ast;
    this.scopeManager = escope.analyze(ast);
    this.scopeManager.attach(); // attach to AST
    this.globalFrame = new Sjedon.StackFrame({
        sjedon: this,
        ast: ast,
        parent: null
    });
    this.currentFrame = this.globalFrame
    this.global = (options && options.global) || {};
}

Sjedon.prototype = new EventEmitter()

Sjedon.prototype.addParents = function (ast) {
    for (var key in ast) if (ast.hasOwnProperty(key)) {
        if (typeof ast[key] === 'object' && ast[key] && !/^_/.test(key)) {
            ast[key]._parent = ast;
            this.addParents(ast[key]);
        }
    }
}

Sjedon.prototype.run = function (block) {
    this.evalBlock(block || this.ast.body);
}

Sjedon.prototype.evalBlock = function (body) {
    var ret = nothing;

    assert(body && typeof body.length === 'number', 'evalBlock: "' + body + '" is not an array of statements')

    for (var i = 0, len = body.length; i < len; i++) {
        ret = this.evalStatement(body[i])
        if (ret !== nothing) { return ret; }
    }

    return nothing;
}

Sjedon.prototype.evalStatement = function (statement) {
    if (statement.type === 'ExpressionStatement') {
        return this.evalExpression(statement.expression);
    } else if(statement.type === 'BlockStatement') {
        return this.evalBlock(statement.body);
    } else if (statement.type === 'ReturnStatement') {
        if (statement.argument !== null) {
            return this.evalExpression(statement.argument)
        }
        return nothing;
    } else if (statement.type === 'BreakStatement') {
        if (statement.label) { notimplemented('Labeled break statement'); }
        return break_;
    } else if (statement.type === 'IfStatement') {
        if (this.evalExpression(statement.test)) {
            return this.evalStatement(statement.consequent);
        } else if (statement.alternate) {
            return this.evalStatement(statement.alternate);
        } else {
            return nothing;
        }
    } else if (statement.type === 'SwitchStatement') {
        var discriminant = this.evalExpression(statement.discriminant);
        var kase;

        for (var i = 0, len = statement.cases.length; i < len; i++) {
            kase = statement.cases[i];
            assert(kase.type === 'SwitchCase');
            if (kase.test === null /* default: */ ||
                    this.evalExpression(kase.test) === discriminant) {
                break;
            }
        }

        for (;i < len; i++) {
            kase = statement.cases[i];
            var ret = this.evalBlock(kase.consequent);
            if (ret === nothing) {
                continue;
            } else if (ret === break_) {
                return nothing;
            } else {
                return ret;
            }
        }

        return nothing;
    } else if (statement.type === 'VariableDeclaration') {
        var self = this;
        statement.declarations.forEach(function (decl) {
            if (decl.init) {
                var initial = self.evalExpression(decl.init)
                self.currentFrame.assignVar(decl.id.name, initial);
            } // else, it's just a placeholder.
        })

        return nothing;
    } else if (statement.type === 'EmptyStatement') {
        return nothing;
    } else {
        notimplemented('statement "' + statement.type + '"');
    }
    assert(false, 'Sjedon#evalStatement must always return explicitly! If there is no "return", "break", or "throw", return a "nothing" reference');
};

Sjedon.prototype.arrayLiteral = function (contents) {
    return contents;
}

Sjedon.prototype.objectLiteral = function (props) {
    var ret = {};
    var len = props.length;
    while(len--) {
        ret[props[len][0]] = props[len][1];
    }
    return ret;
}

Sjedon.prototype.unaryExpression = function (op, argument) {
    if (op === 'void') {
        this.evalExpression(argument);
        return undefined;
    } else if (op === 'typeof') {
        var operand = this.evalExpression(argument);
        return typeof operand;
    }
}

Sjedon.prototype.ternaryExpression = function (test, consequent, alternate) {
    return this.evalExpression(this.evalExpression(test) ? consequent : alternate)
}

Sjedon.prototype.evalObjectKey = function (key) {
    if (key.type === 'Identifier') return key.name
    if (key.type === 'Literal') return key.value

    assert(key.type);
    assert(false, 'unsupported object key: ' + key.type);
}

Sjedon.prototype.evalExpression = function (expr) {
    if (expr.type === 'CallExpression') {
        return this.callFunction(expr.callee);
    } else if (expr.type === 'Literal') {
        return expr.value;
    } else if (expr.type === 'ArrayExpression') {
        return this.arrayLiteral(
            expr.elements.map(this.evalExpression.bind(this)))
    } else if (expr.type === 'ObjectExpression') {
        var self = this;
        return this.objectLiteral(expr.properties.map(function (prop) {
            return [
                self.evalObjectKey(prop.key),
                self.evalExpression(prop.value)
            ];
        }))
    } else if (expr.type === 'AssignmentExpression') {
        return this.currentFrame.assignVar(expr.left.name,
            this.evalExpression(expr.right))
    } else if (expr.type === 'SequenceExpression') {
        for (var i = 0; i < expr.expressions.length - 1; i++) {
            this.evalExpression(expr.expressions[i])
        }
        return this.evalExpression(expr.expressions[i]);
    } else if (expr.type === 'UnaryExpression') {
        return this.unaryExpression(expr.operator, expr.argument);
    } else if (expr.type === 'ConditionalExpression' ) {
        return this.ternaryExpression(expr.test, expr.consequent, expr.alternate);
    } else if (expr.type === 'Identifier') {
        return this.currentFrame.fetchVar(expr.name)
    } else {
        notimplemented('expression type "' + expr.type + '"')
    }
};

Sjedon.prototype.callFunction = function (callee) {
    if (callee.type === 'Identifier') {
        var func = this.currentFrame.fetchVar(callee.name)
        assert(func)
        if (typeof func === 'function') {
            func(null);  // TODO arguments, context
        } else {
            this.runFunction(func)
        }
    } else if (callee.type === 'FunctionExpression' || callee.type === 'FunctionDeclaration') {
        return this.runFunction(callee);
    } else {
        notimplemented('calling functions other than FunctionExpression (given ' + callee.type + ')')
    }
}

Sjedon.prototype.runFunction = function (functionAST) {
    var stackFrame = new Sjedon.StackFrame({
        sjedon: this,
        ast: functionAST,
        parent: this.currentFrame
    })

    this.currentFrame = stackFrame

    var ret = this.evalStatement(functionAST.body)

    this.currentFrame = stackFrame.parent

    return ret === nothing ?
        undefined :
        ret;
}

Sjedon.prototype.findScope = function (funcNode, name) {
    assert(funcNode && name, 'findScope arguments incomplete')
    var scope = this.getScope(funcNode)
    function findName(variable) { return variable.name === name }
    while (scope) {
        if (scope.variables.some(findName)) {
            return scope.block;
        }
        scope = scope.upper
    }
    return null
}

Sjedon.prototype.getScope = function (astNode) {
    return astNode[escope.Scope.mangledName]
}

Sjedon.prototype.scopeVariables = function (astNode) {
    return this.getScope(astNode)
        .variables
        .map(function(v) { return v.name })
}

Sjedon.prototype.trace = function () {
    return this.currentFrame.trace()
}

Sjedon.prototype.fetchVar = function (name) {
    return this.currentFrame.fetchVar(name);
}

module.exports = Sjedon;

<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    yaml = require('yamlparser'),
    _ = require('underscore');

function Skript(filePath) {
    var skriptStr = fs.readFileSync(filePath).toString();
    var skript = yaml.eval(skriptStr);
    skript.id = path.basename(filePath);
    var addNickName = function (actor) {
        actor.nickName = skript.setup.prefix + actor.id;
    }
    _(skript.setup.actors).each(addNickName);
    _(skript.skript).each(function(entry) {
        if (entry.actors)
            _(entry.actors).each(addNickName);
        else if (entry.actor)
            addNickName(entry.actor);
    });
    return skript;
}

exports.loadAllSkripts = function(skriptsPath) {
    var skripts = {};
    fs.readdirSync(skriptsPath).forEach(function(skriptFile) {
        var skriptPath = path.join(skriptsPath, skriptFile);
        var skript = Skript(skriptPath);
        skripts[skript.id] = skript;
    });
    return skripts;
};

<commit_msg>Accept json and yaml<commit_after>var fs = require('fs'),
    path = require('path'),
    yaml = require('yamlparser'),
    _ = require('underscore');

function Skript(filePath) {
    var skriptStr = fs.readFileSync(filePath).toString(), skript;
    if (filePath.indexOf('.json') !== -1) {
        skript = JSON.parse(skriptStr);
    } else {
        skript = yaml.eval(skriptStr);
    }
    skript.id = path.basename(filePath);
    var addNickName = function (actor) {
        actor.nickName = skript.setup.prefix + actor.id;
    };
    _(skript.setup.actors).each(addNickName);
    _(skript.skript).each(function(entry) {
        if (entry.actors)
            _(entry.actors).each(addNickName);
        else if (entry.actor)
            addNickName(entry.actor);
    });
    return skript;
}

exports.loadAllSkripts = function(skriptsPath) {
    var skripts = {};
    fs.readdirSync(skriptsPath).forEach(function(skriptFile) {
        var skriptPath = path.join(skriptsPath, skriptFile);
        var skript = Skript(skriptPath);
        skripts[skript.id] = skript;
    });
    return skripts;
};

<|endoftext|>


<commit_before>if (typeof define !== 'function') { var define = require('amdefine')(module) }
define( function(require,exports,module) {

var _ = require('underscore');
var Events = require('backbone').Events;
var SockJS = require('sockjs-client');
var Command = require('es_command');

var Socket = module.exports = function(channel,data,websocket){
  this.initWebsocket(channel,websocket);
  this.bindDataToSocket(data || {});
};

_.extend(Socket.prototype, Events,{

  initWebsocket: function(channel,websocket){
    var socket = this;
    this.ws = websocket || new SockJS(window.location.protocol + '//' + window.location.host +'/'+ channel +'/pubsub/',{debug:true,devel:true});

    this.ws.onopen = function(e){
      socket.onOpen();
    };
    this.ws.onerror = function(e){
    };
    this.ws.onclose = function(e){
    };
    this.ws.onmessage = _.bind(this.onMessage,this);
  },

  onMessage: function(e){
      var data_string = e.data;
      var c = new Command(data_string);
      var model = this.getModel(c.getDataType(),c.getDataId());

      model.disableSend();
      c.execute(model);
      model.enableSend();
  },

  onOpen: function(){
    this.data.selection.requestReplication();
  },

  send: function(msg){
    this.ws.send(Command.serialize(msg));
  },

  bindDataToSocket: function(data){
    var socket = this;
    socket.data = data;
    for(var type in data){
      data[type].on('send',function(msg){
        socket.send(msg);
      });
    }
  },

  getModel: function(type,id){
    if(id){
      return this.data[type].get(id);
    } else {
      return this.data[type];
    }
  }
});

});
<commit_msg>onOpen now takes a connection handler function as a param <commit_after>if (typeof define !== 'function') { var define = require('amdefine')(module) }
define( function(require,exports,module) {

var _ = require('underscore');
var Events = require('backbone').Events;
var SockJS = require('sockjs-client');
var Command = require('es_command');

var Socket = module.exports = function(channel,data,websocket){
  this.open_handler = function(){};
  this.initWebsocket(channel,websocket);
  this.bindDataToSocket(data || {});
};

_.extend(Socket.prototype, Events,{

  initWebsocket: function(channel,websocket){
    var socket = this;
    this.ws = websocket || new SockJS(window.location.protocol + '//' + window.location.host +'/'+ channel +'/pubsub/',{debug:true,devel:true});

    this.ws.onopen = function(e){
      socket.open_handler();
    };
    this.ws.onerror = function(e){
    };
    this.ws.onclose = function(e){
    };
    this.ws.onmessage = _.bind(this.onMessage,this);
  },

  onMessage: function(e){
      var data_string = e.data;
      var c = new Command(data_string);
      var model = this.getModel(c.getDataType(),c.getDataId());

      model.disableSend();
      c.execute(model);
      model.enableSend();
  },

  onOpen: function(handler){
    this.open_handler = handler;
  },

  send: function(msg){
    this.ws.send(Command.serialize(msg));
  },

  bindDataToSocket: function(data){
    var socket = this;
    socket.data = data;
    for(var type in data){
      data[type].on('send',function(msg){
        socket.send(msg);
      });
    }
  },

  getModel: function(type,id){
    if(id){
      return this.data[type].get(id);
    } else {
      return this.data[type];
    }
  }
});

});
<|endoftext|>


<commit_before>'use strict';

var path = require('path');

function tsifyString(str) {
    return str
        .replace(/[^\w\d _]/g, '')
        .replace(/_/g, ' ')
        .replace(/ (\w)/g, s => s.trim().toUpperCase())
        .replace(/^\w/, s => s.toUpperCase());
}

const INLINE = 0;
const BLOCK = 1;

class Type {
    constructor(st, object) {
        this.st = st;
        this.object = object;

        this.name = this.object.title ? tsifyString(this.object.title) : '';
        this.objName = this.name;

        this.type = INLINE;
        this.converted = false;

        this.inline = null;
        this.block = '';

        this.hasAdditonals = false;
        this.additionals = '';

        if('allOf' in this.object) {
            this.xOf(this.object.anyOf, '&');
        } else if('anyOf' in this.object) {
            this.xOf(this.object.anyOf, '|');
        } else if('oneOf' in this.object) {
            this.xOf(this.object.oneOf, '|');
        }

        if('title' in this.object) {
            this.type = BLOCK;
            this.st.typesDone.push(this);
        }
    }

    xOf(options, separator) {
        let items = options.map((schema) => this.st.parseSchema(schema));

        this.hasAdditonals = true;
        this.additionalsItems = items;
        this.additionalsSep = ` ${separator} `;
    }

    convert() {
        if(this.converted) return;

        this.converted = true;

        if(this.hasAdditonals) {
            this.additionals = this.additionalsItems.map((t) => {
                t.convert();

                if(t.inline !== null) {
                    return t.inline;
                }
            }).join(this.additionalsSep);

            this.inline = this.additionals;
            this.objName = '_' + this.objName;
        }

        if(this.type == INLINE) {
            this.inlinify();
        } else if(this.type == BLOCK) {
            this.blockify();
        }
    }

    inlinify() {
        this.type = INLINE;
    }

    blockify() {
        this.type = BLOCK;

        let add = '';
        if(this.name == this.objName && this.additionals) {
            add = ` & (${this.additionals})`;

            this.block = `type ${this.name} = ${this.inline}${add};`;
            this.inline = this.name;
        } else {
            this.block = `type ${this.name} = ${this.additionals};`;
            this.inline = this.name;
        }
    }
}

class TNumber extends Type {
    inlinify() {
        super.inlinify();
        this.inline = 'number';
    }
}
class TString extends Type {
    inlinify() {
        super.inlinify();
        this.inline = 'string';
    }
}
class TBoolean extends Type {
    inlinify() {
        super.inlinify();
        this.inline = 'boolean';
    }
}
class TNull extends Type {
    inlinify() {
        super.inlinify();
        this.inline = null;
    }
}

class TArray extends Type {
    inlinify() {
        super.inlinify();

        let itemsType = this.st.parseSchema(this.object.items);
        itemsType.convert();

        if(itemsType.inline !== null &&
           itemsType.inline.indexOf(' ') !== -1 &&
           itemsType.inline.substr(-1) !== '}') {
            this.inline = '(' + itemsType.inline + ')[]';
        } else {
            this.inline = itemsType.inline + '[]';
        }
    }
}

class TTypeList extends Type {
    inlinify() {
        super.inlinify();

        this.inline = this.object.type.join(' | ');
    }
}

class TObject extends Type {
    constructor(st, object) {
        super(st, object);

        this.types = [];

        for(let name in this.object.properties) {
            this.types.push({
                name: name,
                type: this.st.parseSchema(this.object.properties[name]),
                required: this.object.required && this.object.required.indexOf(name) > -1
            });
        }
    }

    createTypedef() {
        let typedef = `{\n`;

        for(let t of this.types) {
            let optionalityOperator = '?';
            if(t.required) optionalityOperator = '';

            t.type.convert();
            typedef += `${t.name}${optionalityOperator}: ${t.type.inline};\n`;
        }

        typedef += `}`;
        return typedef;
    }

    inlinify() {
        super.inlinify();

        this.inline = this.createTypedef();
        this.block = '';
    }

    blockify() {
        this.inline = this.name;

        let inlineDef = this.createTypedef();
        let typedef = `interface ${this.objName} ${inlineDef}\n\n`;

        if(this.additionals) {
            let add = '';
            if(this.additionals) {
                add = ` & (${this.additionals})`;
            }

            if(this.objName == this.name) {
                this.objName = '_' + this.name;
            }
        }

        if(this.objName != this.name) {
            this.block = `type ${this.name} = ${this.objName}${this.additionals};\n${typedef}`;
        } else {
            this.block = typedef;
        }

        this.type = BLOCK;
    }
}

class TReference extends Type {
    convert() {
        if(this.converted) return;

        this.converted = true;
        let referenced = this.st.refsDone[this.object.$ref];

        if(!referenced.converted) {
            referenced.convert();
            referenced.blockify();

            this.st.typesDone.push(referenced);
        }

        this.inline = referenced.inline;
    }
}

/**
 * The Sontyp parser object. It's fairly simple, but requires some shared
 * variables between its separate little parsers, so it's a class.
 */
class Sontyp {
    constructor(root) {
        this.root = root;
        this.typesDone = [];
        this.titledTypesDone = {};

        this.schemasTodo = [];
        this.refsDone = {};
    }

    /**
     * A schema is really anything with a type value or some xOf.
     * @param {object} obj - The schema.
     */
    addSchema(schema) {
        this.schemasTodo.push(schema);
    }

    /**
     * Since json-schema does this cool thing called references, we need to be
     * able to resolve the paths in those and load things from these paths.
     * (really just calls Sontyp.parseSchema)
     * @param {string} ref - The path to the thing.
     * @return {Type}
     */
    addByRef(ref) {
        if(this.root) {
            ref = path.join(this.root, ref);
        }
        if(ref in this.refsDone) {
            return;
        }

        let fs = require("fs");
        let file = fs.readFileSync(ref, {encoding: "utf-8"});

        let obj = JSON.parse(file);
        this.parseSchema(obj);
    }

    /**
     * Since json-schema has a fairly complex typing system, we have this
     * separate function to parse anything that could be described as a type
     * definition - a schema.
     * @param {object} schema - The schema.
     * @return {Type} An instance of one of the different possible Type
     * subclasses.
     */
    parseSchema(schema) {
        let type;

        if('id' in schema && schema.id in this.refsDone) {
            return this.refsDone[schema.id];
        }

        if('$ref' in schema) {
            this.addByRef(schema.$ref);
            type = new TReference(this, schema);

        } else if(schema.type && typeof schema.type !== 'string' && schema.type.length) {
            type = new TTypeList(this, schema);

        } else if(schema.type == 'string') {
            type = new TString(this, schema);
        } else if(schema.type == 'number' || schema.type == 'integer') {
            type = new TNumber(this, schema);
        } else if(schema.type == 'boolean') {
            type = new TBoolean(this, schema);

        } else if(schema.type == 'array' || 'items' in schema) {
            type = new TArray(this, schema);
        } else if(schema.type == 'object' || 'properties' in schema) {
            type = new TObject(this, schema);

        } else if('anyOf' in schema || 'allOf' in schema || 'oneOf' in schema) {
            type = new Type(this, schema);
        }

        if('id' in schema) {
            this.refsDone[schema.id] = type;
        }

        return type;
    }

    parse() {
        while(this.schemasTodo.length > 0) {
            let schema = this.schemasTodo.pop();
            let type = this.parseSchema(schema);

            this.typesDone.push(type);
        }

        let typeNames = [];
        let finalTypes = [];
        while(this.typesDone.length > 0) {
            let type = this.typesDone.pop();
            type.convert();

            if(type.type == BLOCK && typeNames.indexOf(type.inline) < 0) {
                typeNames.push(type.inline);
                finalTypes.push(type.block);
            }
        }

        return finalTypes.join('\n\n');
    }
}

function sontyp(json, root) {
    var s = new Sontyp(root);
    s.addSchema(json);
    return s.parse();
}

module.exports = {
    sontyp: sontyp,
    Sontyp: Sontyp
};
module.exports.gulpSontyp = require('./gulp-sontyp');
<commit_msg>Remove all objName code<commit_after>'use strict';

var path = require('path');

function tsifyString(str) {
    return str
        .replace(/[^\w\d _]/g, '')
        .replace(/_/g, ' ')
        .replace(/ (\w)/g, s => s.trim().toUpperCase())
        .replace(/^\w/, s => s.toUpperCase());
}

const INLINE = 0;
const BLOCK = 1;

class Type {
    constructor(st, object) {
        this.st = st;
        this.object = object;

        this.name = this.object.title ? tsifyString(this.object.title) : '';

        this.type = INLINE;
        this.converted = false;

        this.inline = null;
        this.block = '';

        this.hasAdditonals = false;
        this.additionals = '';

        if('allOf' in this.object) {
            this.xOf(this.object.anyOf, '&');
        } else if('anyOf' in this.object) {
            this.xOf(this.object.anyOf, '|');
        } else if('oneOf' in this.object) {
            this.xOf(this.object.oneOf, '|');
        }

        if('title' in this.object) {
            this.type = BLOCK;
            this.st.typesDone.push(this);
        }
    }

    xOf(options, separator) {
        let items = options.map((schema) => this.st.parseSchema(schema));

        this.hasAdditonals = true;
        this.additionalsItems = items;
        this.additionalsSep = ` ${separator} `;
    }

    convert() {
        if(this.converted) return;

        this.converted = true;

        if(this.hasAdditonals) {
            this.additionals = this.additionalsItems.map((t) => {
                t.convert();

                if(t.inline !== null) {
                    return t.inline;
                }
            }).join(this.additionalsSep);

            this.inline = this.additionals;
        }

        if(this.type == INLINE) {
            this.inlinify();
        } else if(this.type == BLOCK) {
            this.blockify();
        }
    }

    inlinify() {
        this.type = INLINE;
    }

    blockify() {
        this.type = BLOCK;

        let add = '';
        if(this.name !== this.inline && this.additionals) {
            add = ` & (${this.additionals})`;

            this.block = `type ${this.name} = ${this.inline}${add};`;
            this.inline = this.name;
        } else {
            this.block = `type ${this.name} = ${this.additionals};`;
            this.inline = this.name;
        }
    }
}

class TNumber extends Type {
    inlinify() {
        super.inlinify();
        this.inline = 'number';
    }
}
class TString extends Type {
    inlinify() {
        super.inlinify();
        this.inline = 'string';
    }
}
class TBoolean extends Type {
    inlinify() {
        super.inlinify();
        this.inline = 'boolean';
    }
}
class TNull extends Type {
    inlinify() {
        super.inlinify();
        this.inline = null;
    }
}

class TArray extends Type {
    inlinify() {
        super.inlinify();

        let itemsType = this.st.parseSchema(this.object.items);
        itemsType.convert();

            this.inline = '(' + itemsType.inline + ')[]';
        } else {
            this.inline = itemsType.inline + '[]';
        }
    }
}

class TTypeList extends Type {
    inlinify() {
        super.inlinify();

        this.inline = this.object.type.join(' | ');
    }
}

class TObject extends Type {
    constructor(st, object) {
        super(st, object);

        this.types = [];

        for(let name in this.object.properties) {
            this.types.push({
                name: name,
                type: this.st.parseSchema(this.object.properties[name]),
                required: this.object.required && this.object.required.indexOf(name) > -1
            });
        }
    }

    createTypedef() {
        let typedef = `{\n`;

        for(let t of this.types) {
            let optionalityOperator = '?';
            if(t.required) optionalityOperator = '';

            t.type.convert();
            typedef += `${t.name}${optionalityOperator}: ${t.type.inline};\n`;
        }

        typedef += `}`;
        return typedef;
    }

    inlinify() {
        super.inlinify();

        this.inline = this.createTypedef();
        this.block = '';
    }

    blockify() {
        this.inline = this.name;

        let inlineDef = this.createTypedef();

        if(this.additionals) {
            this.block = [
                `type ${this.name} = ${inlineDef} & (${this.additionals})`
            ].join('\n');
        } else {
            this.block = `interface ${this.name} ${inlineDef}\n\n`;
        }

        this.type = BLOCK;
    }
}

class TReference extends Type {
    convert() {
        if(this.converted) return;

        this.converted = true;
        let referenced = this.st.refsDone[this.object.$ref];

        if(!referenced.converted) {
            referenced.convert();
            referenced.blockify();

            this.st.typesDone.push(referenced);
        }

        this.inline = referenced.inline;
    }
}

/**
 * The Sontyp parser object. It's fairly simple, but requires some shared
 * variables between its separate little parsers, so it's a class.
 */
class Sontyp {
    constructor(root) {
        this.root = root;
        this.typesDone = [];
        this.titledTypesDone = {};

        this.schemasTodo = [];
        this.refsDone = {};
    }

    /**
     * A schema is really anything with a type value or some xOf.
     * @param {object} obj - The schema.
     */
    addSchema(schema) {
        this.schemasTodo.push(schema);
    }

    /**
     * Since json-schema does this cool thing called references, we need to be
     * able to resolve the paths in those and load things from these paths.
     * (really just calls Sontyp.parseSchema)
     * @param {string} ref - The path to the thing.
     * @return {Type}
     */
    addByRef(ref) {
        if(this.root) {
            ref = path.join(this.root, ref);
        }
        if(ref in this.refsDone) {
            return;
        }

        let fs = require("fs");
        let file = fs.readFileSync(ref, {encoding: "utf-8"});

        let obj = JSON.parse(file);
        this.parseSchema(obj);
    }

    /**
     * Since json-schema has a fairly complex typing system, we have this
     * separate function to parse anything that could be described as a type
     * definition - a schema.
     * @param {object} schema - The schema.
     * @return {Type} An instance of one of the different possible Type
     * subclasses.
     */
    parseSchema(schema) {
        let type;

        if('id' in schema && schema.id in this.refsDone) {
            return this.refsDone[schema.id];
        }

        if('$ref' in schema) {
            this.addByRef(schema.$ref);
            type = new TReference(this, schema);

        } else if(schema.type && typeof schema.type !== 'string' && schema.type.length) {
            type = new TTypeList(this, schema);

        } else if(schema.type == 'string') {
            type = new TString(this, schema);
        } else if(schema.type == 'number' || schema.type == 'integer') {
            type = new TNumber(this, schema);
        } else if(schema.type == 'boolean') {
            type = new TBoolean(this, schema);

        } else if(schema.type == 'array' || 'items' in schema) {
            type = new TArray(this, schema);
        } else if(schema.type == 'object' || 'properties' in schema) {
            type = new TObject(this, schema);

        } else if('anyOf' in schema || 'allOf' in schema || 'oneOf' in schema) {
            type = new Type(this, schema);
        }

        if('id' in schema) {
            this.refsDone[schema.id] = type;
        }

        return type;
    }

    parse() {
        while(this.schemasTodo.length > 0) {
            let schema = this.schemasTodo.pop();
            let type = this.parseSchema(schema);

            this.typesDone.push(type);
        }

        let typeNames = [];
        let finalTypes = [];
        while(this.typesDone.length > 0) {
            let type = this.typesDone.pop();
            type.convert();

            if(type.type == BLOCK && typeNames.indexOf(type.inline) < 0) {
                typeNames.push(type.inline);
                finalTypes.push(type.block);
            }
        }

        return finalTypes.join('\n\n');
    }
}

function sontyp(json, root) {
    var s = new Sontyp(root);
    s.addSchema(json);
    return s.parse();
}

module.exports = {
    sontyp: sontyp,
    Sontyp: Sontyp
};
module.exports.gulpSontyp = require('./gulp-sontyp');
<|endoftext|>